]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/validation.rs
Reflect API changes on current master
[rust.git] / src / librustc_mir / transform / check_consts / validation.rs
1 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
2
3 use rustc_errors::struct_span_err;
4 use rustc_hir::lang_items;
5 use rustc_hir::{def_id::DefId, HirId};
6 use rustc_infer::infer::TyCtxtInferExt;
7 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
8 use rustc_middle::mir::*;
9 use rustc_middle::ty::cast::CastTy;
10 use rustc_middle::ty::{self, Instance, InstanceDef, TyCtxt};
11 use rustc_span::Span;
12 use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
13 use rustc_trait_selection::traits::{self, TraitEngine};
14
15 use std::borrow::Cow;
16 use std::ops::Deref;
17
18 use super::ops::{self, NonConstOp};
19 use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop};
20 use super::resolver::FlowSensitiveAnalysis;
21 use super::{is_lang_panic_fn, ConstCx, ConstKind, Qualif};
22 use crate::const_eval::{is_const_fn, is_unstable_const_fn};
23 use crate::dataflow::MaybeMutBorrowedLocals;
24 use crate::dataflow::{self, Analysis};
25
26 // We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
27 // through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`
28 // kills locals upon `StorageDead` because a local will never be used after a `StorageDead`.
29 type IndirectlyMutableResults<'mir, 'tcx> =
30     dataflow::ResultsCursor<'mir, 'tcx, MaybeMutBorrowedLocals<'mir, 'tcx>>;
31
32 type QualifResults<'mir, 'tcx, Q> =
33     dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
34
35 #[derive(Default)]
36 pub struct Qualifs<'mir, 'tcx> {
37     has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
38     needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
39     indirectly_mutable: Option<IndirectlyMutableResults<'mir, 'tcx>>,
40 }
41
42 impl Qualifs<'mir, 'tcx> {
43     fn indirectly_mutable(
44         &mut self,
45         ccx: &'mir ConstCx<'mir, 'tcx>,
46         local: Local,
47         location: Location,
48     ) -> bool {
49         let indirectly_mutable = self.indirectly_mutable.get_or_insert_with(|| {
50             let ConstCx { tcx, body, def_id, param_env, .. } = *ccx;
51
52             // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not
53             // allowed in a const.
54             //
55             // FIXME(ecstaticmorse): Someday we want to allow custom drop impls. How do we do this
56             // without breaking stable code?
57             MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env)
58                 .unsound_ignore_borrow_on_drop()
59                 .into_engine(tcx, &body, def_id)
60                 .iterate_to_fixpoint()
61                 .into_results_cursor(&body)
62         });
63
64         indirectly_mutable.seek_before_primary_effect(location);
65         indirectly_mutable.get().contains(local)
66     }
67
68     /// Returns `true` if `local` is `NeedsDrop` at the given `Location`.
69     ///
70     /// Only updates the cursor if absolutely necessary
71     fn needs_drop(
72         &mut self,
73         ccx: &'mir ConstCx<'mir, 'tcx>,
74         local: Local,
75         location: Location,
76     ) -> bool {
77         let ty = ccx.body.local_decls[local].ty;
78         if !NeedsDrop::in_any_value_of_ty(ccx, ty) {
79             return false;
80         }
81
82         let needs_drop = self.needs_drop.get_or_insert_with(|| {
83             let ConstCx { tcx, body, def_id, .. } = *ccx;
84
85             FlowSensitiveAnalysis::new(NeedsDrop, ccx)
86                 .into_engine(tcx, &body, def_id)
87                 .iterate_to_fixpoint()
88                 .into_results_cursor(&body)
89         });
90
91         needs_drop.seek_before_primary_effect(location);
92         needs_drop.get().contains(local) || self.indirectly_mutable(ccx, local, location)
93     }
94
95     /// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
96     ///
97     /// Only updates the cursor if absolutely necessary.
98     fn has_mut_interior(
99         &mut self,
100         ccx: &'mir ConstCx<'mir, 'tcx>,
101         local: Local,
102         location: Location,
103     ) -> bool {
104         let ty = ccx.body.local_decls[local].ty;
105         if !HasMutInterior::in_any_value_of_ty(ccx, ty) {
106             return false;
107         }
108
109         let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| {
110             let ConstCx { tcx, body, def_id, .. } = *ccx;
111
112             FlowSensitiveAnalysis::new(HasMutInterior, ccx)
113                 .into_engine(tcx, &body, def_id)
114                 .iterate_to_fixpoint()
115                 .into_results_cursor(&body)
116         });
117
118         has_mut_interior.seek_before_primary_effect(location);
119         has_mut_interior.get().contains(local) || self.indirectly_mutable(ccx, local, location)
120     }
121
122     fn in_return_place(&mut self, ccx: &'mir ConstCx<'mir, 'tcx>) -> ConstQualifs {
123         // Find the `Return` terminator if one exists.
124         //
125         // If no `Return` terminator exists, this MIR is divergent. Just return the conservative
126         // qualifs for the return type.
127         let return_block = ccx
128             .body
129             .basic_blocks()
130             .iter_enumerated()
131             .find(|(_, block)| match block.terminator().kind {
132                 TerminatorKind::Return => true,
133                 _ => false,
134             })
135             .map(|(bb, _)| bb);
136
137         let return_block = match return_block {
138             None => return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty()),
139             Some(bb) => bb,
140         };
141
142         let return_loc = ccx.body.terminator_loc(return_block);
143
144         let custom_eq = match ccx.const_kind() {
145             // We don't care whether a `const fn` returns a value that is not structurally
146             // matchable. Functions calls are opaque and always use type-based qualification, so
147             // this value should never be used.
148             ConstKind::ConstFn => true,
149
150             // If we know that all values of the return type are structurally matchable, there's no
151             // need to run dataflow.
152             ConstKind::Const | ConstKind::Static | ConstKind::StaticMut
153                 if !CustomEq::in_any_value_of_ty(ccx, ccx.body.return_ty()) =>
154             {
155                 false
156             }
157
158             ConstKind::Const | ConstKind::Static | ConstKind::StaticMut => {
159                 let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx)
160                     .into_engine(ccx.tcx, &ccx.body, ccx.def_id)
161                     .iterate_to_fixpoint()
162                     .into_results_cursor(&ccx.body);
163
164                 cursor.seek_after_primary_effect(return_loc);
165                 cursor.contains(RETURN_PLACE)
166             }
167         };
168
169         ConstQualifs {
170             needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
171             has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
172             custom_eq,
173         }
174     }
175 }
176
177 pub struct Validator<'mir, 'tcx> {
178     ccx: &'mir ConstCx<'mir, 'tcx>,
179     qualifs: Qualifs<'mir, 'tcx>,
180
181     /// The span of the current statement.
182     span: Span,
183 }
184
185 impl Deref for Validator<'mir, 'tcx> {
186     type Target = ConstCx<'mir, 'tcx>;
187
188     fn deref(&self) -> &Self::Target {
189         &self.ccx
190     }
191 }
192
193 impl Validator<'mir, 'tcx> {
194     pub fn new(ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
195         Validator { span: ccx.body.span, ccx, qualifs: Default::default() }
196     }
197
198     pub fn check_body(&mut self) {
199         let ConstCx { tcx, body, def_id, const_kind, .. } = *self.ccx;
200
201         let use_min_const_fn_checks = (const_kind == Some(ConstKind::ConstFn)
202             && crate::const_eval::is_min_const_fn(tcx, def_id))
203             && !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
204
205         if use_min_const_fn_checks {
206             // Enforce `min_const_fn` for stable `const fn`s.
207             use crate::transform::qualify_min_const_fn::is_min_const_fn;
208             if let Err((span, err)) = is_min_const_fn(tcx, def_id, &body) {
209                 error_min_const_fn_violation(tcx, span, err);
210                 return;
211             }
212         }
213
214         check_short_circuiting_in_const_local(self.ccx);
215
216         if body.is_cfg_cyclic() {
217             // We can't provide a good span for the error here, but this should be caught by the
218             // HIR const-checker anyways.
219             self.check_op_spanned(ops::Loop, body.span);
220         }
221
222         self.visit_body(&body);
223
224         // Ensure that the end result is `Sync` in a non-thread local `static`.
225         let should_check_for_sync =
226             const_kind == Some(ConstKind::Static) && !tcx.is_thread_local_static(def_id);
227
228         if should_check_for_sync {
229             let hir_id = tcx.hir().as_local_hir_id(def_id.expect_local());
230             check_return_ty_is_sync(tcx, &body, hir_id);
231         }
232     }
233
234     pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
235         self.qualifs.in_return_place(self.ccx)
236     }
237
238     /// Emits an error at the given `span` if an expression cannot be evaluated in the current
239     /// context.
240     pub fn check_op_spanned<O>(&mut self, op: O, span: Span)
241     where
242         O: NonConstOp,
243     {
244         debug!("check_op: op={:?}", op);
245
246         if op.is_allowed_in_item(self) {
247             return;
248         }
249
250         // If an operation is supported in miri (and is not already controlled by a feature gate) it
251         // can be turned on with `-Zunleash-the-miri-inside-of-you`.
252         let is_unleashable = O::IS_SUPPORTED_IN_MIRI && O::feature_gate().is_none();
253
254         if is_unleashable && self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
255             self.tcx.sess.span_warn(span, "skipping const checks");
256             return;
257         }
258
259         op.emit_error(self, span);
260     }
261
262     /// Emits an error if an expression cannot be evaluated in the current context.
263     pub fn check_op(&mut self, op: impl NonConstOp) {
264         let span = self.span;
265         self.check_op_spanned(op, span)
266     }
267
268     fn check_static(&mut self, def_id: DefId, span: Span) {
269         if self.tcx.is_thread_local_static(def_id) {
270             self.check_op_spanned(ops::ThreadLocalAccess, span)
271         } else {
272             self.check_op_spanned(ops::StaticAccess, span)
273         }
274     }
275 }
276
277 impl Visitor<'tcx> for Validator<'mir, 'tcx> {
278     fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) {
279         trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup);
280
281         // Just as the old checker did, we skip const-checking basic blocks on the unwind path.
282         // These blocks often drop locals that would otherwise be returned from the function.
283         //
284         // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler
285         // error anyway, but maybe we should do more here?
286         if block.is_cleanup {
287             return;
288         }
289
290         self.super_basic_block_data(bb, block);
291     }
292
293     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
294         trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
295
296         // Special-case reborrows to be more like a copy of a reference.
297         match *rvalue {
298             Rvalue::Ref(_, kind, place) => {
299                 if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
300                     let ctx = match kind {
301                         BorrowKind::Shared => {
302                             PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
303                         }
304                         BorrowKind::Shallow => {
305                             PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
306                         }
307                         BorrowKind::Unique => {
308                             PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
309                         }
310                         BorrowKind::Mut { .. } => {
311                             PlaceContext::MutatingUse(MutatingUseContext::Borrow)
312                         }
313                     };
314                     self.visit_local(&place.local, ctx, location);
315                     self.visit_projection(place.local, reborrowed_proj, ctx, location);
316                     return;
317                 }
318             }
319             Rvalue::AddressOf(mutbl, place) => {
320                 if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
321                     let ctx = match mutbl {
322                         Mutability::Not => {
323                             PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
324                         }
325                         Mutability::Mut => PlaceContext::MutatingUse(MutatingUseContext::AddressOf),
326                     };
327                     self.visit_local(&place.local, ctx, location);
328                     self.visit_projection(place.local, reborrowed_proj, ctx, location);
329                     return;
330                 }
331             }
332             _ => {}
333         }
334
335         self.super_rvalue(rvalue, location);
336
337         match *rvalue {
338             Rvalue::Use(_)
339             | Rvalue::Repeat(..)
340             | Rvalue::UnaryOp(UnOp::Neg, _)
341             | Rvalue::UnaryOp(UnOp::Not, _)
342             | Rvalue::NullaryOp(NullOp::SizeOf, _)
343             | Rvalue::CheckedBinaryOp(..)
344             | Rvalue::Cast(CastKind::Pointer(_), ..)
345             | Rvalue::Discriminant(..)
346             | Rvalue::Len(_)
347             | Rvalue::Aggregate(..) => {}
348
349             Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
350             | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
351                 let ty = place.ty(self.body, self.tcx).ty;
352                 let is_allowed = match ty.kind {
353                     // Inside a `static mut`, `&mut [...]` is allowed.
354                     ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut => {
355                         true
356                     }
357
358                     // FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
359                     // that this is merely a ZST and it is already eligible for promotion.
360                     // This may require an RFC?
361                     /*
362                     ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
363                         => true,
364                     */
365                     _ => false,
366                 };
367
368                 if !is_allowed {
369                     if let BorrowKind::Mut { .. } = kind {
370                         self.check_op(ops::MutBorrow);
371                     } else {
372                         self.check_op(ops::CellBorrow);
373                     }
374                 }
375             }
376
377             Rvalue::AddressOf(Mutability::Mut, _) => self.check_op(ops::MutAddressOf),
378
379             Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
380             | Rvalue::AddressOf(Mutability::Not, ref place) => {
381                 let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
382                     &self.ccx,
383                     &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
384                     place.as_ref(),
385                 );
386
387                 if borrowed_place_has_mut_interior {
388                     self.check_op(ops::CellBorrow);
389                 }
390             }
391
392             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
393                 let operand_ty = operand.ty(self.body, self.tcx);
394                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
395                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
396
397                 if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
398                     self.check_op(ops::RawPtrToIntCast);
399                 }
400             }
401
402             Rvalue::BinaryOp(op, ref lhs, _) => {
403                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
404                     assert!(
405                         op == BinOp::Eq
406                             || op == BinOp::Ne
407                             || op == BinOp::Le
408                             || op == BinOp::Lt
409                             || op == BinOp::Ge
410                             || op == BinOp::Gt
411                             || op == BinOp::Offset
412                     );
413
414                     self.check_op(ops::RawPtrComparison);
415                 }
416             }
417
418             Rvalue::NullaryOp(NullOp::Box, _) => {
419                 self.check_op(ops::HeapAllocation);
420             }
421         }
422     }
423
424     fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) {
425         self.super_operand(op, location);
426         if let Operand::Constant(c) = op {
427             if let Some(def_id) = c.check_static_ptr(self.tcx) {
428                 self.check_static(def_id, self.span);
429             }
430         }
431     }
432     fn visit_projection_elem(
433         &mut self,
434         place_local: Local,
435         proj_base: &[PlaceElem<'tcx>],
436         elem: &PlaceElem<'tcx>,
437         context: PlaceContext,
438         location: Location,
439     ) {
440         trace!(
441             "visit_projection_elem: place_local={:?} proj_base={:?} elem={:?} \
442             context={:?} location={:?}",
443             place_local,
444             proj_base,
445             elem,
446             context,
447             location,
448         );
449
450         self.super_projection_elem(place_local, proj_base, elem, context, location);
451
452         match elem {
453             ProjectionElem::Deref => {
454                 let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
455                 if let ty::RawPtr(_) = base_ty.kind {
456                     if proj_base.is_empty() {
457                         if let (local, []) = (place_local, proj_base) {
458                             let decl = &self.body.local_decls[local];
459                             if let LocalInfo::StaticRef { def_id, .. } = decl.local_info {
460                                 let span = decl.source_info.span;
461                                 self.check_static(def_id, span);
462                                 return;
463                             }
464                         }
465                     }
466                     self.check_op(ops::RawPtrDeref);
467                 }
468
469                 if context.is_mutating_use() {
470                     self.check_op(ops::MutDeref);
471                 }
472             }
473
474             ProjectionElem::ConstantIndex { .. }
475             | ProjectionElem::Downcast(..)
476             | ProjectionElem::Subslice { .. }
477             | ProjectionElem::Field(..)
478             | ProjectionElem::Index(_) => {
479                 let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
480                 match base_ty.ty_adt_def() {
481                     Some(def) if def.is_union() => {
482                         self.check_op(ops::UnionAccess);
483                     }
484
485                     _ => {}
486                 }
487             }
488         }
489     }
490
491     fn visit_source_info(&mut self, source_info: &SourceInfo) {
492         trace!("visit_source_info: source_info={:?}", source_info);
493         self.span = source_info.span;
494     }
495
496     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
497         trace!("visit_statement: statement={:?} location={:?}", statement, location);
498
499         match statement.kind {
500             StatementKind::Assign(..) | StatementKind::SetDiscriminant { .. } => {
501                 self.super_statement(statement, location);
502             }
503
504             StatementKind::FakeRead(
505                 FakeReadCause::ForMatchedPlace
506                 | FakeReadCause::ForMatchGuard
507                 | FakeReadCause::ForGuardBinding,
508                 _,
509             ) => {
510                 self.super_statement(statement, location);
511                 self.check_op(ops::IfOrMatch);
512             }
513             StatementKind::LlvmInlineAsm { .. } => {
514                 self.super_statement(statement, location);
515                 self.check_op(ops::InlineAsm);
516             }
517
518             StatementKind::FakeRead(FakeReadCause::ForLet | FakeReadCause::ForIndex, _)
519             | StatementKind::StorageLive(_)
520             | StatementKind::StorageDead(_)
521             | StatementKind::Retag { .. }
522             | StatementKind::AscribeUserType(..)
523             | StatementKind::Nop => {}
524         }
525     }
526
527     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
528         trace!("visit_terminator: terminator={:?} location={:?}", terminator, location);
529         self.super_terminator(terminator, location);
530
531         match &terminator.kind {
532             TerminatorKind::Call { func, .. } => {
533                 let fn_ty = func.ty(self.body, self.tcx);
534
535                 let (def_id, substs) = match fn_ty.kind {
536                     ty::FnDef(def_id, substs) => (def_id, substs),
537
538                     ty::FnPtr(_) => {
539                         self.check_op(ops::FnCallIndirect);
540                         return;
541                     }
542                     _ => {
543                         span_bug!(terminator.source_info.span, "invalid callee of type {:?}", fn_ty)
544                     }
545                 };
546
547                 // At this point, we are calling a function whose `DefId` is known...
548                 if is_const_fn(self.tcx, def_id) {
549                     return;
550                 }
551
552                 // See if this is a trait method for a concrete type whose impl of that trait is
553                 // `const`.
554                 if self.tcx.features().const_trait_impl {
555                     let instance = Instance::resolve(self.tcx, self.param_env, def_id, substs);
556                     debug!("Resolving ({:?}) -> {:?}", def_id, instance);
557                     if let Ok(Some(func)) = instance {
558                         if let InstanceDef::Item(def_id) = func.def {
559                             if is_const_fn(self.tcx, def_id) {
560                                 return;
561                             }
562                         }
563                     }
564                 }
565
566                 if is_lang_panic_fn(self.tcx, def_id) {
567                     self.check_op(ops::Panic);
568                 } else if let Some(feature) = is_unstable_const_fn(self.tcx, def_id) {
569                     // Exempt unstable const fns inside of macros with
570                     // `#[allow_internal_unstable]`.
571                     if !self.span.allows_unstable(feature) {
572                         self.check_op(ops::FnCallUnstable(def_id, feature));
573                     }
574                 } else {
575                     self.check_op(ops::FnCallNonConst(def_id));
576                 }
577             }
578
579             // Forbid all `Drop` terminators unless the place being dropped is a local with no
580             // projections that cannot be `NeedsDrop`.
581             TerminatorKind::Drop { location: dropped_place, .. }
582             | TerminatorKind::DropAndReplace { location: dropped_place, .. } => {
583                 let mut err_span = self.span;
584
585                 // Check to see if the type of this place can ever have a drop impl. If not, this
586                 // `Drop` terminator is frivolous.
587                 let ty_needs_drop =
588                     dropped_place.ty(self.body, self.tcx).ty.needs_drop(self.tcx, self.param_env);
589
590                 if !ty_needs_drop {
591                     return;
592                 }
593
594                 let needs_drop = if let Some(local) = dropped_place.as_local() {
595                     // Use the span where the local was declared as the span of the drop error.
596                     err_span = self.body.local_decls[local].source_info.span;
597                     self.qualifs.needs_drop(self.ccx, local, location)
598                 } else {
599                     true
600                 };
601
602                 if needs_drop {
603                     self.check_op_spanned(ops::LiveDrop, err_span);
604                 }
605             }
606
607             // FIXME: Some of these are only caught by `min_const_fn`, but should error here
608             // instead.
609             TerminatorKind::Abort
610             | TerminatorKind::Assert { .. }
611             | TerminatorKind::FalseEdges { .. }
612             | TerminatorKind::FalseUnwind { .. }
613             | TerminatorKind::GeneratorDrop
614             | TerminatorKind::Goto { .. }
615             | TerminatorKind::Resume
616             | TerminatorKind::Return
617             | TerminatorKind::SwitchInt { .. }
618             | TerminatorKind::Unreachable
619             | TerminatorKind::Yield { .. } => {}
620         }
621     }
622 }
623
624 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
625     struct_span_err!(tcx.sess, span, E0723, "{}", msg)
626         .note(
627             "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
628              for more information",
629         )
630         .help("add `#![feature(const_fn)]` to the crate attributes to enable")
631         .emit();
632 }
633
634 fn check_short_circuiting_in_const_local(ccx: &ConstCx<'_, 'tcx>) {
635     let body = ccx.body;
636
637     if body.control_flow_destroyed.is_empty() {
638         return;
639     }
640
641     let mut locals = body.vars_iter();
642     if let Some(local) = locals.next() {
643         let span = body.local_decls[local].source_info.span;
644         let mut error = ccx.tcx.sess.struct_span_err(
645             span,
646             &format!(
647                 "new features like let bindings are not permitted in {}s \
648                 which also use short circuiting operators",
649                 ccx.const_kind(),
650             ),
651         );
652         for (span, kind) in body.control_flow_destroyed.iter() {
653             error.span_note(
654                 *span,
655                 &format!(
656                     "use of {} here does not actually short circuit due to \
657                      the const evaluator presently not being able to do control flow. \
658                      See issue #49146 <https://github.com/rust-lang/rust/issues/49146> \
659                      for more information.",
660                     kind
661                 ),
662             );
663         }
664         for local in locals {
665             let span = body.local_decls[local].source_info.span;
666             error.span_note(span, "more locals are defined here");
667         }
668         error.emit();
669     }
670 }
671
672 fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId) {
673     let ty = body.return_ty();
674     tcx.infer_ctxt().enter(|infcx| {
675         let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
676         let mut fulfillment_cx = traits::FulfillmentContext::new();
677         let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
678         fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
679         if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
680             infcx.report_fulfillment_errors(&err, None, false);
681         }
682     });
683 }
684
685 fn place_as_reborrow(
686     tcx: TyCtxt<'tcx>,
687     body: &Body<'tcx>,
688     place: Place<'tcx>,
689 ) -> Option<&'a [PlaceElem<'tcx>]> {
690     place.projection.split_last().and_then(|(outermost, inner)| {
691         if outermost != &ProjectionElem::Deref {
692             return None;
693         }
694
695         // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
696         // that points to the allocation for the static. Don't treat these as reborrows.
697         if body.local_decls[place.local].is_ref_to_static() {
698             return None;
699         }
700
701         // Ensure the type being derefed is a reference and not a raw pointer.
702         //
703         // This is sufficient to prevent an access to a `static mut` from being marked as a
704         // reborrow, even if the check above were to disappear.
705         let inner_ty = Place::ty_from(place.local, inner, body, tcx).ty;
706         match inner_ty.kind {
707             ty::Ref(..) => Some(inner),
708             _ => None,
709         }
710     })
711 }