]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/qualify_consts.rs
Auto merge of #66004 - eddyb:revert-early-gate, r=petrochenkov
[rust.git] / src / librustc_mir / transform / qualify_consts.rs
1 //! A pass that qualifies constness of temporaries in constants,
2 //! static initializers and functions and also drives promotion.
3 //!
4 //! The Qualif flags below can be used to also provide better
5 //! diagnostics as to why a constant rvalue wasn't promoted.
6
7 use rustc_index::bit_set::BitSet;
8 use rustc_index::vec::IndexVec;
9 use rustc_data_structures::fx::FxHashSet;
10 use rustc_target::spec::abi::Abi;
11 use rustc::hir;
12 use rustc::hir::def_id::DefId;
13 use rustc::traits::{self, TraitEngine};
14 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
15 use rustc::ty::cast::CastTy;
16 use rustc::ty::query::Providers;
17 use rustc::mir::*;
18 use rustc::mir::interpret::ConstValue;
19 use rustc::mir::traversal::ReversePostorder;
20 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
21 use rustc::middle::lang_items;
22 use rustc::session::config::nightly_options;
23 use syntax::ast::LitKind;
24 use syntax::feature_gate::{emit_feature_err, GateIssue};
25 use syntax::symbol::sym;
26 use syntax_pos::{Span, DUMMY_SP};
27
28 use std::borrow::Cow;
29 use std::cell::Cell;
30 use std::fmt;
31 use std::ops::{Deref, Index, IndexMut};
32 use std::usize;
33
34 use rustc::hir::HirId;
35 use crate::transform::{MirPass, MirSource};
36 use super::promote_consts::{self, Candidate, TempState};
37 use crate::transform::check_consts::ops::{self, NonConstOp};
38
39 /// What kind of item we are in.
40 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
41 enum Mode {
42     /// A `static` item.
43     Static,
44     /// A `static mut` item.
45     StaticMut,
46     /// A `const fn` item.
47     ConstFn,
48     /// A `const` item or an anonymous constant (e.g. in array lengths).
49     Const,
50     /// Other type of `fn`.
51     NonConstFn,
52 }
53
54 impl Mode {
55     /// Determine whether we have to do full const-checking because syntactically, we
56     /// are required to be "const".
57     #[inline]
58     fn requires_const_checking(self) -> bool {
59         self != Mode::NonConstFn
60     }
61 }
62
63 impl fmt::Display for Mode {
64     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65         match *self {
66             Mode::Const => write!(f, "constant"),
67             Mode::Static | Mode::StaticMut => write!(f, "static"),
68             Mode::ConstFn => write!(f, "constant function"),
69             Mode::NonConstFn => write!(f, "function")
70         }
71     }
72 }
73
74 const QUALIF_COUNT: usize = 4;
75
76 // FIXME(eddyb) once we can use const generics, replace this array with
77 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
78 #[derive(Copy, Clone, Default)]
79 struct PerQualif<T>([T; QUALIF_COUNT]);
80
81 impl<T: Clone> PerQualif<T> {
82     fn new(x: T) -> Self {
83         PerQualif([x.clone(), x.clone(), x.clone(), x])
84     }
85 }
86
87 impl<T> PerQualif<T> {
88     fn as_mut(&mut self) -> PerQualif<&mut T> {
89         let [x0, x1, x2, x3] = &mut self.0;
90         PerQualif([x0, x1, x2, x3])
91     }
92
93     fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
94         let [x0, x1, x2, x3] = self.0;
95         let [y0, y1, y2, y3] = other.0;
96         PerQualif([(x0, y0), (x1, y1), (x2, y2), (x3, y3)])
97     }
98 }
99
100 impl PerQualif<bool> {
101     fn encode_to_bits(self) -> u8 {
102         self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
103             bits | ((qualif as u8) << i)
104         })
105     }
106
107     fn decode_from_bits(bits: u8) -> Self {
108         let mut qualifs = Self::default();
109         for (i, qualif) in qualifs.0.iter_mut().enumerate() {
110             *qualif = (bits & (1 << i)) != 0;
111         }
112         qualifs
113     }
114 }
115
116 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
117     type Output = T;
118
119     fn index(&self, _: Q) -> &T {
120         &self.0[Q::IDX]
121     }
122 }
123
124 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
125     fn index_mut(&mut self, _: Q) -> &mut T {
126         &mut self.0[Q::IDX]
127     }
128 }
129
130 struct ConstCx<'a, 'tcx> {
131     tcx: TyCtxt<'tcx>,
132     param_env: ty::ParamEnv<'tcx>,
133     mode: Mode,
134     body: &'a Body<'tcx>,
135
136     per_local: PerQualif<BitSet<Local>>,
137 }
138
139 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
140     fn is_const_panic_fn(&self, def_id: DefId) -> bool {
141         Some(def_id) == self.tcx.lang_items().panic_fn() ||
142         Some(def_id) == self.tcx.lang_items().begin_panic_fn()
143     }
144 }
145
146 #[derive(Copy, Clone, Debug)]
147 enum ValueSource<'a, 'tcx> {
148     Rvalue(&'a Rvalue<'tcx>),
149     DropAndReplace(&'a Operand<'tcx>),
150     Call {
151         callee: &'a Operand<'tcx>,
152         args: &'a [Operand<'tcx>],
153         return_ty: Ty<'tcx>,
154     },
155 }
156
157 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
158 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
159 /// "I found something bad, no reason to go on searching". `false` is only returned if we
160 /// definitely cannot find anything bad anywhere.
161 ///
162 /// The default implementations proceed structurally.
163 trait Qualif {
164     const IDX: usize;
165
166     /// Return the qualification that is (conservatively) correct for any value
167     /// of the type, or `None` if the qualification is not value/type-based.
168     fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
169         None
170     }
171
172     /// Return a mask for the qualification, given a type. This is `false` iff
173     /// no value of that type can have the qualification.
174     fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
175         Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
176     }
177
178     fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
179         cx.per_local.0[Self::IDX].contains(local)
180     }
181
182     fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
183         // FIXME(eddyb) should we do anything here for value properties?
184         false
185     }
186
187     fn in_projection_structurally(
188         cx: &ConstCx<'_, 'tcx>,
189         place: PlaceRef<'_, 'tcx>,
190     ) -> bool {
191         if let [proj_base @ .., elem] = place.projection {
192             let base_qualif = Self::in_place(cx, PlaceRef {
193                 base: place.base,
194                 projection: proj_base,
195             });
196             let qualif = base_qualif && Self::mask_for_ty(
197                 cx,
198                 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
199                     .projection_ty(cx.tcx, elem)
200                     .ty,
201             );
202             match elem {
203                 ProjectionElem::Deref |
204                 ProjectionElem::Subslice { .. } |
205                 ProjectionElem::Field(..) |
206                 ProjectionElem::ConstantIndex { .. } |
207                 ProjectionElem::Downcast(..) => qualif,
208
209                 // FIXME(eddyb) shouldn't this be masked *after* including the
210                 // index local? Then again, it's `usize` which is neither
211                 // `HasMutInterior` nor `NeedsDrop`.
212                 ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
213             }
214         } else {
215             bug!("This should be called if projection is not empty");
216         }
217     }
218
219     fn in_projection(
220         cx: &ConstCx<'_, 'tcx>,
221         place: PlaceRef<'_, 'tcx>,
222     ) -> bool {
223         Self::in_projection_structurally(cx, place)
224     }
225
226     fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
227         match place {
228             PlaceRef {
229                 base: PlaceBase::Local(local),
230                 projection: [],
231             } => Self::in_local(cx, *local),
232             PlaceRef {
233                 base: PlaceBase::Static(box Static {
234                     kind: StaticKind::Promoted(..),
235                     ..
236                 }),
237                 projection: [],
238             } => bug!("qualifying already promoted MIR"),
239             PlaceRef {
240                 base: PlaceBase::Static(static_),
241                 projection: [],
242             } => {
243                 Self::in_static(cx, static_)
244             },
245             PlaceRef {
246                 base: _,
247                 projection: [.., _],
248             } => Self::in_projection(cx, place),
249         }
250     }
251
252     fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
253         match *operand {
254             Operand::Copy(ref place) |
255             Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
256
257             Operand::Constant(ref constant) => {
258                 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
259                     // Don't peek inside trait associated constants.
260                     if cx.tcx.trait_of_item(def_id).is_some() {
261                         Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
262                     } else {
263                         let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
264
265                         let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
266
267                         // Just in case the type is more specific than
268                         // the definition, e.g., impl associated const
269                         // with type parameters, take it into account.
270                         qualif && Self::mask_for_ty(cx, constant.literal.ty)
271                     }
272                 } else {
273                     false
274                 }
275             }
276         }
277     }
278
279     fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
280         match *rvalue {
281             Rvalue::NullaryOp(..) => false,
282
283             Rvalue::Discriminant(ref place) |
284             Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
285
286             Rvalue::Use(ref operand) |
287             Rvalue::Repeat(ref operand, _) |
288             Rvalue::UnaryOp(_, ref operand) |
289             Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
290
291             Rvalue::BinaryOp(_, ref lhs, ref rhs) |
292             Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
293                 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
294             }
295
296             Rvalue::Ref(_, _, ref place) => {
297                 // Special-case reborrows to be more like a copy of the reference.
298                 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
299                     if ProjectionElem::Deref == elem {
300                         let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
301                         if let ty::Ref(..) = base_ty.kind {
302                             return Self::in_place(cx, PlaceRef {
303                                 base: &place.base,
304                                 projection: proj_base,
305                             });
306                         }
307                     }
308                 }
309
310                 Self::in_place(cx, place.as_ref())
311             }
312
313             Rvalue::Aggregate(_, ref operands) => {
314                 operands.iter().any(|o| Self::in_operand(cx, o))
315             }
316         }
317     }
318
319     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
320         Self::in_rvalue_structurally(cx, rvalue)
321     }
322
323     fn in_call(
324         cx: &ConstCx<'_, 'tcx>,
325         _callee: &Operand<'tcx>,
326         _args: &[Operand<'tcx>],
327         return_ty: Ty<'tcx>,
328     ) -> bool {
329         // Be conservative about the returned value of a const fn.
330         Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
331     }
332
333     fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
334         match source {
335             ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
336             ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
337             ValueSource::Call { callee, args, return_ty } => {
338                 Self::in_call(cx, callee, args, return_ty)
339             }
340         }
341     }
342 }
343
344 /// Constant containing interior mutability (`UnsafeCell<T>`).
345 /// This must be ruled out to make sure that evaluating the constant at compile-time
346 /// and at *any point* during the run-time would produce the same result. In particular,
347 /// promotion of temporaries must not change program behavior; if the promoted could be
348 /// written to, that would be a problem.
349 struct HasMutInterior;
350
351 impl Qualif for HasMutInterior {
352     const IDX: usize = 0;
353
354     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
355         Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
356     }
357
358     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
359         match *rvalue {
360             // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
361             // allowed in constants (and the `Checker` will error), and/or it
362             // won't be promoted, due to `&mut ...` or interior mutability.
363             Rvalue::Ref(_, kind, ref place) => {
364                 let ty = place.ty(cx.body, cx.tcx).ty;
365
366                 if let BorrowKind::Mut { .. } = kind {
367                     // In theory, any zero-sized value could be borrowed
368                     // mutably without consequences. However, only &mut []
369                     // is allowed right now, and only in functions.
370                     if cx.mode == Mode::StaticMut {
371                         // Inside a `static mut`, &mut [...] is also allowed.
372                         match ty.kind {
373                             ty::Array(..) | ty::Slice(_) => {}
374                             _ => return true,
375                         }
376                     } else if let ty::Array(_, len) = ty.kind {
377                         // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
378                         // seems unnecessary, given that this is merely a ZST.
379                         match len.try_eval_usize(cx.tcx, cx.param_env) {
380                             Some(0) if cx.mode == Mode::NonConstFn => {},
381                             _ => return true,
382                         }
383                     } else {
384                         return true;
385                     }
386                 }
387             }
388
389             Rvalue::Aggregate(ref kind, _) => {
390                 if let AggregateKind::Adt(def, ..) = **kind {
391                     if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
392                         let ty = rvalue.ty(cx.body, cx.tcx);
393                         assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
394                         return true;
395                     }
396                 }
397             }
398
399             _ => {}
400         }
401
402         Self::in_rvalue_structurally(cx, rvalue)
403     }
404 }
405
406 /// Constant containing an ADT that implements `Drop`.
407 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
408 /// as that might not be a `const fn`, and (b) because implicit promotion would
409 /// remove side-effects that occur as part of dropping that value.
410 struct NeedsDrop;
411
412 impl Qualif for NeedsDrop {
413     const IDX: usize = 1;
414
415     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
416         Some(ty.needs_drop(cx.tcx, cx.param_env))
417     }
418
419     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
420         if let Rvalue::Aggregate(ref kind, _) = *rvalue {
421             if let AggregateKind::Adt(def, ..) = **kind {
422                 if def.has_dtor(cx.tcx) {
423                     return true;
424                 }
425             }
426         }
427
428         Self::in_rvalue_structurally(cx, rvalue)
429     }
430 }
431
432 /// Not promotable at all - non-`const fn` calls, `asm!`,
433 /// pointer comparisons, ptr-to-int casts, etc.
434 /// Inside a const context all constness rules apply, so promotion simply has to follow the regular
435 /// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
436 /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
437 /// visitor enforces by emitting errors when working in const context.
438 struct IsNotPromotable;
439
440 impl Qualif for IsNotPromotable {
441     const IDX: usize = 2;
442
443     fn in_static(cx: &ConstCx<'_, 'tcx>, static_: &Static<'tcx>) -> bool {
444         match static_.kind {
445             StaticKind::Promoted(_, _) => unreachable!(),
446             StaticKind::Static => {
447                 // Only allow statics (not consts) to refer to other statics.
448                 // FIXME(eddyb) does this matter at all for promotion?
449                 let allowed = cx.mode == Mode::Static || cx.mode == Mode::StaticMut;
450
451                 !allowed ||
452                     cx.tcx.get_attrs(static_.def_id).iter().any(
453                         |attr| attr.check_name(sym::thread_local)
454                     )
455             }
456         }
457     }
458
459     fn in_projection(
460         cx: &ConstCx<'_, 'tcx>,
461         place: PlaceRef<'_, 'tcx>,
462     ) -> bool {
463         if let [proj_base @ .., elem] = place.projection {
464             match elem {
465                 ProjectionElem::Deref |
466                 ProjectionElem::Downcast(..) => return true,
467
468                 ProjectionElem::ConstantIndex {..} |
469                 ProjectionElem::Subslice {..} |
470                 ProjectionElem::Index(_) => {}
471
472                 ProjectionElem::Field(..) => {
473                     if cx.mode == Mode::NonConstFn {
474                         let base_ty = Place::ty_from(place.base, proj_base, cx.body, cx.tcx).ty;
475                         if let Some(def) = base_ty.ty_adt_def() {
476                             // No promotion of union field accesses.
477                             if def.is_union() {
478                                 return true;
479                             }
480                         }
481                     }
482                 }
483             }
484
485             Self::in_projection_structurally(cx, place)
486         } else {
487             bug!("This should be called if projection is not empty");
488         }
489     }
490
491     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
492         match *rvalue {
493             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
494                 let operand_ty = operand.ty(cx.body, cx.tcx);
495                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
496                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
497                 match (cast_in, cast_out) {
498                     (CastTy::Ptr(_), CastTy::Int(_)) |
499                     (CastTy::FnPtr, CastTy::Int(_)) => {
500                         // in normal functions, mark such casts as not promotable
501                         return true;
502                     }
503                     _ => {}
504                 }
505             }
506
507             Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
508                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.body, cx.tcx).kind {
509                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
510                             op == BinOp::Le || op == BinOp::Lt ||
511                             op == BinOp::Ge || op == BinOp::Gt ||
512                             op == BinOp::Offset);
513
514                     // raw pointer operations are not allowed inside promoteds
515                     return true;
516                 }
517             }
518
519             Rvalue::NullaryOp(NullOp::Box, _) => return true,
520
521             _ => {}
522         }
523
524         Self::in_rvalue_structurally(cx, rvalue)
525     }
526
527     fn in_call(
528         cx: &ConstCx<'_, 'tcx>,
529         callee: &Operand<'tcx>,
530         args: &[Operand<'tcx>],
531         _return_ty: Ty<'tcx>,
532     ) -> bool {
533         let fn_ty = callee.ty(cx.body, cx.tcx);
534         match fn_ty.kind {
535             ty::FnDef(def_id, _) => {
536                 match cx.tcx.fn_sig(def_id).abi() {
537                     Abi::RustIntrinsic |
538                     Abi::PlatformIntrinsic => {
539                         assert!(!cx.tcx.is_const_fn(def_id));
540                         match &cx.tcx.item_name(def_id).as_str()[..] {
541                             | "size_of"
542                             | "min_align_of"
543                             | "needs_drop"
544                             | "type_id"
545                             | "bswap"
546                             | "bitreverse"
547                             | "ctpop"
548                             | "cttz"
549                             | "cttz_nonzero"
550                             | "ctlz"
551                             | "ctlz_nonzero"
552                             | "wrapping_add"
553                             | "wrapping_sub"
554                             | "wrapping_mul"
555                             | "unchecked_shl"
556                             | "unchecked_shr"
557                             | "rotate_left"
558                             | "rotate_right"
559                             | "add_with_overflow"
560                             | "sub_with_overflow"
561                             | "mul_with_overflow"
562                             | "saturating_add"
563                             | "saturating_sub"
564                             | "transmute"
565                             | "simd_insert"
566                             | "simd_extract"
567                             => return true,
568
569                             _ => {}
570                         }
571                     }
572                     _ => {
573                         let is_const_fn =
574                             cx.tcx.is_const_fn(def_id) ||
575                             cx.tcx.is_unstable_const_fn(def_id).is_some() ||
576                             cx.is_const_panic_fn(def_id);
577                         if !is_const_fn {
578                             return true;
579                         }
580                     }
581                 }
582             }
583             _ => return true,
584         }
585
586         Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
587     }
588 }
589
590 /// Refers to temporaries which cannot be promoted *implicitly*.
591 /// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
592 /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
593 /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
594 /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
595 struct IsNotImplicitlyPromotable;
596
597 impl Qualif for IsNotImplicitlyPromotable {
598     const IDX: usize = 3;
599
600     fn in_call(
601         cx: &ConstCx<'_, 'tcx>,
602         callee: &Operand<'tcx>,
603         args: &[Operand<'tcx>],
604         _return_ty: Ty<'tcx>,
605     ) -> bool {
606         if cx.mode == Mode::NonConstFn {
607             if let ty::FnDef(def_id, _) = callee.ty(cx.body, cx.tcx).kind {
608                 // Never promote runtime `const fn` calls of
609                 // functions without `#[rustc_promotable]`.
610                 if !cx.tcx.is_promotable_const_fn(def_id) {
611                     return true;
612                 }
613             }
614         }
615
616         Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
617     }
618 }
619
620 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
621 macro_rules! static_assert_seq_qualifs {
622     ($i:expr => $first:ident $(, $rest:ident)*) => {
623         static_assert!({
624             static_assert_seq_qualifs!($i + 1 => $($rest),*);
625
626             $first::IDX == $i
627         });
628     };
629     ($i:expr =>) => {
630         static_assert!(QUALIF_COUNT == $i);
631     };
632 }
633 static_assert_seq_qualifs!(
634     0 => HasMutInterior, NeedsDrop, IsNotPromotable, IsNotImplicitlyPromotable
635 );
636
637 impl ConstCx<'_, 'tcx> {
638     fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
639         let mut qualifs = PerQualif::default();
640         qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
641         qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
642         qualifs[IsNotPromotable] = IsNotPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
643         qualifs[IsNotImplicitlyPromotable] =
644             IsNotImplicitlyPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
645         qualifs
646     }
647
648     fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
649         let mut qualifs = PerQualif::default();
650         qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
651         qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
652         qualifs[IsNotPromotable] = IsNotPromotable::in_local(self, local);
653         qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_local(self, local);
654         qualifs
655     }
656
657     fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
658         let mut qualifs = PerQualif::default();
659         qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
660         qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
661         qualifs[IsNotPromotable] = IsNotPromotable::in_value(self, source);
662         qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_value(self, source);
663         qualifs
664     }
665 }
666
667 /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
668 /// for value qualifications, and accumulates writes of
669 /// rvalue/call results to locals, in `local_qualif`.
670 /// It also records candidates for promotion in `promotion_candidates`,
671 /// both in functions and const/static items.
672 struct Checker<'a, 'tcx> {
673     cx: ConstCx<'a, 'tcx>,
674
675     span: Span,
676     def_id: DefId,
677     rpo: ReversePostorder<'a, 'tcx>,
678
679     temp_promotion_state: IndexVec<Local, TempState>,
680     promotion_candidates: Vec<Candidate>,
681     unchecked_promotion_candidates: Vec<Candidate>,
682
683     /// If `true`, do not emit errors to the user, merely collect them in `errors`.
684     suppress_errors: bool,
685     errors: Vec<(Span, String)>,
686 }
687
688 macro_rules! unleash_miri {
689     ($this:expr) => {{
690         if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
691             if $this.mode.requires_const_checking() && !$this.suppress_errors {
692                 $this.tcx.sess.span_warn($this.span, "skipping const checks");
693             }
694             return;
695         }
696     }}
697 }
698
699 impl Deref for Checker<'a, 'tcx> {
700     type Target = ConstCx<'a, 'tcx>;
701
702     fn deref(&self) -> &Self::Target {
703         &self.cx
704     }
705 }
706
707 impl<'a, 'tcx> Checker<'a, 'tcx> {
708     fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
709         assert!(def_id.is_local());
710         let mut rpo = traversal::reverse_postorder(body);
711         let (temps, unchecked_promotion_candidates) =
712             promote_consts::collect_temps_and_candidates(tcx, body, &mut rpo);
713         rpo.reset();
714
715         let param_env = tcx.param_env(def_id);
716
717         let mut cx = ConstCx {
718             tcx,
719             param_env,
720             mode,
721             body,
722             per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
723         };
724
725         for (local, decl) in body.local_decls.iter_enumerated() {
726             if let LocalKind::Arg = body.local_kind(local) {
727                 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
728                 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
729                     if *qualif {
730                         per_local.insert(local);
731                     }
732                 }
733             }
734             if !temps[local].is_promotable() {
735                 cx.per_local[IsNotPromotable].insert(local);
736             }
737             if let LocalKind::Var = body.local_kind(local) {
738                 // Sanity check to prevent implicit and explicit promotion of
739                 // named locals
740                 assert!(cx.per_local[IsNotPromotable].contains(local));
741             }
742         }
743
744         Checker {
745             cx,
746             span: body.span,
747             def_id,
748             rpo,
749             temp_promotion_state: temps,
750             promotion_candidates: vec![],
751             unchecked_promotion_candidates,
752             errors: vec![],
753             suppress_errors: false,
754         }
755     }
756
757     // FIXME(eddyb) we could split the errors into meaningful
758     // categories, but enabling full miri would make that
759     // slightly pointless (even with feature-gating).
760     fn not_const(&mut self, op: impl NonConstOp) {
761         unleash_miri!(self);
762         if self.mode.requires_const_checking() && !self.suppress_errors {
763             self.record_error(op);
764             let mut err = struct_span_err!(
765                 self.tcx.sess,
766                 self.span,
767                 E0019,
768                 "{} contains unimplemented expression type",
769                 self.mode
770             );
771             if self.tcx.sess.teach(&err.get_code().unwrap()) {
772                 err.note("A function call isn't allowed in the const's initialization expression \
773                           because the expression's value must be known at compile-time.");
774                 err.note("Remember: you can't use a function call inside a const's initialization \
775                           expression! However, you can use it anywhere else.");
776             }
777             err.emit();
778         }
779     }
780
781     fn record_error(&mut self, op: impl NonConstOp) {
782         self.record_error_spanned(op, self.span);
783     }
784
785     fn record_error_spanned(&mut self, op: impl NonConstOp, span: Span) {
786         self.errors.push((span, format!("{:?}", op)));
787     }
788
789     /// Assigns an rvalue/call qualification to the given destination.
790     fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
791         trace!("assign: {:?} <- {:?}", dest, source);
792
793         let mut qualifs = self.qualifs_in_value(source);
794
795         match source {
796             ValueSource::Rvalue(&Rvalue::Ref(_, kind, ref place)) => {
797                 // Getting `true` from `HasMutInterior::in_rvalue` means
798                 // the borrowed place is disallowed from being borrowed,
799                 // due to either a mutable borrow (with some exceptions),
800                 // or an shared borrow of a value with interior mutability.
801                 // Then `HasMutInterior` is replaced with `IsNotPromotable`,
802                 // to avoid duplicate errors (e.g. from reborrowing).
803                 if qualifs[HasMutInterior] {
804                     qualifs[HasMutInterior] = false;
805                     qualifs[IsNotPromotable] = true;
806
807                     debug!("suppress_errors: {}", self.suppress_errors);
808                     if self.mode.requires_const_checking() && !self.suppress_errors {
809                         if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
810                             self.record_error(ops::MutBorrow(kind));
811                             if let BorrowKind::Mut { .. } = kind {
812                                 let mut err = struct_span_err!(self.tcx.sess,  self.span, E0017,
813                                                                "references in {}s may only refer \
814                                                                 to immutable values", self.mode);
815                                 err.span_label(self.span, format!("{}s require immutable values",
816                                                                     self.mode));
817                                 if self.tcx.sess.teach(&err.get_code().unwrap()) {
818                                     err.note("References in statics and constants may only refer \
819                                               to immutable values.\n\n\
820                                               Statics are shared everywhere, and if they refer to \
821                                               mutable data one might violate memory safety since \
822                                               holding multiple mutable references to shared data \
823                                               is not allowed.\n\n\
824                                               If you really want global mutable state, try using \
825                                               static mut or a global UnsafeCell.");
826                                 }
827                                 err.emit();
828                             } else {
829                                 span_err!(self.tcx.sess, self.span, E0492,
830                                           "cannot borrow a constant which may contain \
831                                            interior mutability, create a static instead");
832                             }
833                         }
834                     }
835                 } else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
836                     // Don't promote BorrowKind::Shallow borrows, as they don't
837                     // reach codegen.
838                     // FIXME(eddyb) the two other kinds of borrow (`Shallow` and `Unique`)
839                     // aren't promoted here but *could* be promoted as part of a larger
840                     // value because `IsNotPromotable` isn't being set for them,
841                     // need to figure out what is the intended behavior.
842
843                     // We might have a candidate for promotion.
844                     let candidate = Candidate::Ref(location);
845                     // Start by traversing to the "base", with non-deref projections removed.
846                     let deref_proj =
847                         place.projection.iter().rev().find(|&elem| *elem == ProjectionElem::Deref);
848
849                     debug!(
850                         "qualify_consts: promotion candidate: place={:?} {:?}",
851                         place.base, deref_proj
852                     );
853                     // We can only promote interior borrows of promotable temps (non-temps
854                     // don't get promoted anyway).
855                     // (If we bailed out of the loop due to a `Deref` above, we will definitely
856                     // not enter the conditional here.)
857                     if let (PlaceBase::Local(local), None) = (&place.base, deref_proj) {
858                         if self.body.local_kind(*local) == LocalKind::Temp {
859                             debug!("qualify_consts: promotion candidate: local={:?}", local);
860                             // The borrowed place doesn't have `HasMutInterior`
861                             // (from `in_rvalue`), so we can safely ignore
862                             // `HasMutInterior` from the local's qualifications.
863                             // This allows borrowing fields which don't have
864                             // `HasMutInterior`, from a type that does, e.g.:
865                             // `let _: &'static _ = &(Cell::new(1), 2).1;`
866                             let mut local_qualifs = self.qualifs_in_local(*local);
867                             // Any qualifications, except HasMutInterior (see above), disqualify
868                             // from promotion.
869                             // This is, in particular, the "implicit promotion" version of
870                             // the check making sure that we don't run drop glue during const-eval.
871                             local_qualifs[HasMutInterior] = false;
872                             if !local_qualifs.0.iter().any(|&qualif| qualif) {
873                                 debug!("qualify_consts: promotion candidate: {:?}", candidate);
874                                 self.promotion_candidates.push(candidate);
875                             }
876                         }
877                     }
878                 }
879             },
880             ValueSource::Rvalue(&Rvalue::Repeat(ref operand, _)) => {
881                 debug!("assign: self.cx.mode={:?} self.def_id={:?} location={:?} operand={:?}",
882                        self.cx.mode, self.def_id, location, operand);
883                 if self.should_promote_repeat_expression(operand) &&
884                         self.tcx.features().const_in_array_repeat_expressions {
885                     self.promotion_candidates.push(Candidate::Repeat(location));
886                 }
887             },
888             _ => {},
889         }
890
891         let mut dest_projection = &dest.projection[..];
892         let index = loop {
893             match (&dest.base, dest_projection) {
894                 // We treat all locals equal in constants
895                 (&PlaceBase::Local(index), []) => break index,
896                 // projections are transparent for assignments
897                 // we qualify the entire destination at once, even if just a field would have
898                 // stricter qualification
899                 (base, [proj_base @ .., _]) => {
900                     // Catch more errors in the destination. `visit_place` also checks various
901                     // projection rules like union field access and raw pointer deref
902                     let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
903                     self.visit_place_base(base, context, location);
904                     self.visit_projection(base, dest_projection, context, location);
905                     dest_projection = proj_base;
906                 },
907                 (&PlaceBase::Static(box Static {
908                     kind: StaticKind::Promoted(..),
909                     ..
910                 }), []) => bug!("promoteds don't exist yet during promotion"),
911                 (&PlaceBase::Static(box Static{ kind: _, .. }), []) => {
912                     // Catch more errors in the destination. `visit_place` also checks that we
913                     // do not try to access statics from constants or try to mutate statics
914                     let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
915                     self.visit_place_base(&dest.base, context, location);
916                     return;
917                 }
918             }
919         };
920
921         let kind = self.body.local_kind(index);
922         debug!("store to {:?} {:?}", kind, index);
923
924         // Only handle promotable temps in non-const functions.
925         if self.mode == Mode::NonConstFn {
926             if kind != LocalKind::Temp ||
927                !self.temp_promotion_state[index].is_promotable() {
928                 return;
929             }
930         }
931
932         // this is overly restrictive, because even full assignments do not clear the qualif
933         // While we could special case full assignments, this would be inconsistent with
934         // aggregates where we overwrite all fields via assignments, which would not get
935         // that feature.
936         for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
937             if *qualif {
938                 per_local.insert(index);
939             }
940         }
941
942         // Ensure the `IsNotPromotable` qualification is preserved.
943         // NOTE(eddyb) this is actually unnecessary right now, as
944         // we never replace the local's qualif, but we might in
945         // the future, and so it serves to catch changes that unset
946         // important bits (in which case, asserting `contains` could
947         // be replaced with calling `insert` to re-set the bit).
948         if kind == LocalKind::Temp {
949             if !self.temp_promotion_state[index].is_promotable() {
950                 assert!(self.cx.per_local[IsNotPromotable].contains(index));
951             }
952         }
953     }
954
955     /// Check a whole const, static initializer or const fn.
956     fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
957         use crate::transform::check_consts as new_checker;
958
959         debug!("const-checking {} {:?}", self.mode, self.def_id);
960
961         // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are
962         // enabled.
963         let use_new_validator = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
964         if use_new_validator {
965             debug!("Using dataflow-based const validator");
966         }
967
968         let item = new_checker::Item::new(self.tcx, self.def_id, self.body);
969         let mut validator = new_checker::validation::Validator::new(&item);
970
971         validator.suppress_errors = !use_new_validator;
972         self.suppress_errors = use_new_validator;
973
974         let body = self.body;
975
976         let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
977         let mut bb = START_BLOCK;
978         let mut has_controlflow_error = false;
979         loop {
980             seen_blocks.insert(bb.index());
981
982             self.visit_basic_block_data(bb, &body[bb]);
983             validator.visit_basic_block_data(bb, &body[bb]);
984
985             let target = match body[bb].terminator().kind {
986                 TerminatorKind::Goto { target } |
987                 TerminatorKind::FalseUnwind { real_target: target, .. } |
988                 TerminatorKind::Drop { target, .. } |
989                 TerminatorKind::DropAndReplace { target, .. } |
990                 TerminatorKind::Assert { target, .. } |
991                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
992                     Some(target)
993                 }
994
995                 // Non-terminating calls cannot produce any value.
996                 TerminatorKind::Call { destination: None, .. } => {
997                     break;
998                 }
999
1000                 TerminatorKind::SwitchInt {..} |
1001                 TerminatorKind::Resume |
1002                 TerminatorKind::Abort |
1003                 TerminatorKind::GeneratorDrop |
1004                 TerminatorKind::Yield { .. } |
1005                 TerminatorKind::Unreachable |
1006                 TerminatorKind::FalseEdges { .. } => None,
1007
1008                 TerminatorKind::Return => {
1009                     break;
1010                 }
1011             };
1012
1013             match target {
1014                 // No loops allowed.
1015                 Some(target) if !seen_blocks.contains(target.index()) => {
1016                     bb = target;
1017                 }
1018                 _ => {
1019                     has_controlflow_error = true;
1020                     self.not_const(ops::Loop);
1021                     validator.check_op(ops::Loop);
1022                     break;
1023                 }
1024             }
1025         }
1026
1027         // The new validation pass should agree with the old when running on simple const bodies
1028         // (e.g. no `if` or `loop`).
1029         if !use_new_validator {
1030             let mut new_errors = validator.take_errors();
1031
1032             // FIXME: each checker sometimes emits the same error with the same span twice in a row.
1033             self.errors.dedup();
1034             new_errors.dedup();
1035
1036             if self.errors != new_errors {
1037                 validator_mismatch(
1038                     self.tcx,
1039                     body,
1040                     std::mem::replace(&mut self.errors, vec![]),
1041                     new_errors,
1042                 );
1043             }
1044         }
1045
1046         // Collect all the temps we need to promote.
1047         let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
1048
1049         // HACK(eddyb) don't try to validate promotion candidates if any
1050         // parts of the control-flow graph were skipped due to an error.
1051         let promotion_candidates = if has_controlflow_error {
1052             let unleash_miri = self
1053                 .tcx
1054                 .sess
1055                 .opts
1056                 .debugging_opts
1057                 .unleash_the_miri_inside_of_you;
1058             if !unleash_miri {
1059                 self.tcx.sess.delay_span_bug(
1060                     body.span,
1061                     "check_const: expected control-flow error(s)",
1062                 );
1063             }
1064             self.promotion_candidates.clone()
1065         } else {
1066             self.valid_promotion_candidates()
1067         };
1068         debug!("qualify_const: promotion_candidates={:?}", promotion_candidates);
1069         for candidate in promotion_candidates {
1070             match candidate {
1071                 Candidate::Repeat(Location { block: bb, statement_index: stmt_idx }) => {
1072                     if let StatementKind::Assign(box(_, Rvalue::Repeat(
1073                         Operand::Move(place),
1074                         _
1075                     ))) = &self.body[bb].statements[stmt_idx].kind {
1076                         if let Some(index) = place.as_local() {
1077                             promoted_temps.insert(index);
1078                         }
1079                     }
1080                 }
1081                 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
1082                     if let StatementKind::Assign(
1083                         box(
1084                             _,
1085                             Rvalue::Ref(_, _, place)
1086                         )
1087                     ) = &self.body[bb].statements[stmt_idx].kind {
1088                         if let Some(index) = place.as_local() {
1089                             promoted_temps.insert(index);
1090                         }
1091                     }
1092                 }
1093                 Candidate::Argument { .. } => {}
1094             }
1095         }
1096
1097         let mut qualifs = self.qualifs_in_local(RETURN_PLACE);
1098
1099         // Account for errors in consts by using the
1100         // conservative type qualification instead.
1101         if qualifs[IsNotPromotable] {
1102             qualifs = self.qualifs_in_any_value_of_ty(body.return_ty());
1103         }
1104
1105         (qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
1106     }
1107
1108     /// Get the subset of `unchecked_promotion_candidates` that are eligible
1109     /// for promotion.
1110     // FIXME(eddyb) replace the old candidate gathering with this.
1111     fn valid_promotion_candidates(&self) -> Vec<Candidate> {
1112         // Sanity-check the promotion candidates.
1113         let candidates = promote_consts::validate_candidates(
1114             self.tcx,
1115             self.body,
1116             self.def_id,
1117             &self.temp_promotion_state,
1118             &self.unchecked_promotion_candidates,
1119         );
1120
1121         if candidates != self.promotion_candidates {
1122             let report = |msg, candidate| {
1123                 let span = match candidate {
1124                     Candidate::Ref(loc) |
1125                     Candidate::Repeat(loc) => self.body.source_info(loc).span,
1126                     Candidate::Argument { bb, .. } => {
1127                         self.body[bb].terminator().source_info.span
1128                     }
1129                 };
1130                 self.tcx.sess.span_err(span, &format!("{}: {:?}", msg, candidate));
1131             };
1132
1133             for &c in &self.promotion_candidates {
1134                 if !candidates.contains(&c) {
1135                     report("invalidated old candidate", c);
1136                 }
1137             }
1138
1139             for &c in &candidates {
1140                 if !self.promotion_candidates.contains(&c) {
1141                     report("extra new candidate", c);
1142                 }
1143             }
1144
1145             bug!("promotion candidate validation mismatches (see above)");
1146         }
1147
1148         candidates
1149     }
1150
1151     /// Returns `true` if the operand of a repeat expression is promotable.
1152     fn should_promote_repeat_expression(&self, operand: &Operand<'tcx>) -> bool {
1153         let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) ||
1154                              IsNotPromotable::in_operand(self, operand);
1155         debug!("should_promote_repeat_expression: operand={:?} not_promotable={:?}",
1156                operand, not_promotable);
1157         !not_promotable
1158     }
1159 }
1160
1161 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1162     fn visit_place_base(
1163         &mut self,
1164         place_base: &PlaceBase<'tcx>,
1165         context: PlaceContext,
1166         location: Location,
1167     ) {
1168         self.super_place_base(place_base, context, location);
1169         match place_base {
1170             PlaceBase::Local(_) => {}
1171             PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
1172                 unreachable!()
1173             }
1174             PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
1175                 if self.tcx
1176                         .get_attrs(*def_id)
1177                         .iter()
1178                         .any(|attr| attr.check_name(sym::thread_local)) {
1179                     if self.mode.requires_const_checking() && !self.suppress_errors {
1180                         self.record_error(ops::ThreadLocalAccess);
1181                         span_err!(self.tcx.sess, self.span, E0625,
1182                                     "thread-local statics cannot be \
1183                                     accessed at compile-time");
1184                     }
1185                     return;
1186                 }
1187
1188                 // Only allow statics (not consts) to refer to other statics.
1189                 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
1190                     if self.mode == Mode::Static
1191                         && context.is_mutating_use()
1192                         && !self.suppress_errors
1193                     {
1194                         // this is not strictly necessary as miri will also bail out
1195                         // For interior mutability we can't really catch this statically as that
1196                         // goes through raw pointers and intermediate temporaries, so miri has
1197                         // to catch this anyway
1198                         self.tcx.sess.span_err(
1199                             self.span,
1200                             "cannot mutate statics in the initializer of another static",
1201                         );
1202                     }
1203                     return;
1204                 }
1205                 unleash_miri!(self);
1206
1207                 if self.mode.requires_const_checking() && !self.suppress_errors {
1208                     self.record_error(ops::StaticAccess);
1209                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
1210                                                     "{}s cannot refer to statics, use \
1211                                                     a constant instead", self.mode);
1212                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
1213                         err.note(
1214                             "Static and const variables can refer to other const variables. \
1215                                 But a const variable cannot refer to a static variable."
1216                         );
1217                         err.help(
1218                             "To fix this, the value can be extracted as a const and then used."
1219                         );
1220                     }
1221                     err.emit()
1222                 }
1223             }
1224         }
1225     }
1226
1227     fn visit_projection_elem(
1228         &mut self,
1229         place_base: &PlaceBase<'tcx>,
1230         proj_base: &[PlaceElem<'tcx>],
1231         elem: &PlaceElem<'tcx>,
1232         context: PlaceContext,
1233         location: Location,
1234     ) {
1235         debug!(
1236             "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \
1237             context={:?} location={:?}",
1238             place_base,
1239             proj_base,
1240             elem,
1241             context,
1242             location,
1243         );
1244
1245         self.super_projection_elem(place_base, proj_base, elem, context, location);
1246
1247         match elem {
1248             ProjectionElem::Deref => {
1249                 if context.is_mutating_use() {
1250                     // `not_const` errors out in const contexts
1251                     self.not_const(ops::MutDeref)
1252                 }
1253                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
1254                 match self.mode {
1255                     Mode::NonConstFn => {}
1256                     _ if self.suppress_errors => {}
1257                     _ => {
1258                         if let ty::RawPtr(_) = base_ty.kind {
1259                             if !self.tcx.features().const_raw_ptr_deref {
1260                                 self.record_error(ops::RawPtrDeref);
1261                                 emit_feature_err(
1262                                     &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
1263                                     self.span, GateIssue::Language,
1264                                     &format!(
1265                                         "dereferencing raw pointers in {}s is unstable",
1266                                         self.mode,
1267                                     ),
1268                                 );
1269                             }
1270                         }
1271                     }
1272                 }
1273             }
1274
1275             ProjectionElem::ConstantIndex {..} |
1276             ProjectionElem::Subslice {..} |
1277             ProjectionElem::Field(..) |
1278             ProjectionElem::Index(_) => {
1279                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
1280                 if let Some(def) = base_ty.ty_adt_def() {
1281                     if def.is_union() {
1282                         match self.mode {
1283                             Mode::ConstFn => {
1284                                 if !self.tcx.features().const_fn_union
1285                                     && !self.suppress_errors
1286                                 {
1287                                     self.record_error(ops::UnionAccess);
1288                                     emit_feature_err(
1289                                         &self.tcx.sess.parse_sess, sym::const_fn_union,
1290                                         self.span, GateIssue::Language,
1291                                         "unions in const fn are unstable",
1292                                     );
1293                                 }
1294                             },
1295
1296                             | Mode::NonConstFn
1297                             | Mode::Static
1298                             | Mode::StaticMut
1299                             | Mode::Const
1300                             => {},
1301                         }
1302                     }
1303                 }
1304             }
1305
1306             ProjectionElem::Downcast(..) => {
1307                 self.not_const(ops::Downcast)
1308             }
1309         }
1310     }
1311
1312     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
1313         debug!("visit_operand: operand={:?} location={:?}", operand, location);
1314         self.super_operand(operand, location);
1315
1316         match *operand {
1317             Operand::Move(ref place) => {
1318                 // Mark the consumed locals to indicate later drops are noops.
1319                 if let Some(local) = place.as_local() {
1320                     self.cx.per_local[NeedsDrop].remove(local);
1321                 }
1322             }
1323             Operand::Copy(_) |
1324             Operand::Constant(_) => {}
1325         }
1326     }
1327
1328     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1329         debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
1330
1331         // Check nested operands and places.
1332         if let Rvalue::Ref(_, kind, ref place) = *rvalue {
1333             // Special-case reborrows.
1334             let mut reborrow_place = None;
1335             if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
1336                 if elem == ProjectionElem::Deref {
1337                     let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
1338                     if let ty::Ref(..) = base_ty.kind {
1339                         reborrow_place = Some(proj_base);
1340                     }
1341                 }
1342             }
1343
1344             if let Some(proj) = reborrow_place {
1345                 let ctx = match kind {
1346                     BorrowKind::Shared => PlaceContext::NonMutatingUse(
1347                         NonMutatingUseContext::SharedBorrow,
1348                     ),
1349                     BorrowKind::Shallow => PlaceContext::NonMutatingUse(
1350                         NonMutatingUseContext::ShallowBorrow,
1351                     ),
1352                     BorrowKind::Unique => PlaceContext::NonMutatingUse(
1353                         NonMutatingUseContext::UniqueBorrow,
1354                     ),
1355                     BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
1356                         MutatingUseContext::Borrow,
1357                     ),
1358                 };
1359                 self.visit_place_base(&place.base, ctx, location);
1360                 self.visit_projection(&place.base, proj, ctx, location);
1361             } else {
1362                 self.super_rvalue(rvalue, location);
1363             }
1364         } else {
1365             self.super_rvalue(rvalue, location);
1366         }
1367
1368         match *rvalue {
1369             Rvalue::Use(_) |
1370             Rvalue::Repeat(..) |
1371             Rvalue::UnaryOp(UnOp::Neg, _) |
1372             Rvalue::UnaryOp(UnOp::Not, _) |
1373             Rvalue::NullaryOp(NullOp::SizeOf, _) |
1374             Rvalue::CheckedBinaryOp(..) |
1375             Rvalue::Cast(CastKind::Pointer(_), ..) |
1376             Rvalue::Discriminant(..) |
1377             Rvalue::Len(_) |
1378             Rvalue::Ref(..) |
1379             Rvalue::Aggregate(..) => {}
1380
1381             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
1382                 let operand_ty = operand.ty(self.body, self.tcx);
1383                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
1384                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
1385                 match (cast_in, cast_out) {
1386                     (CastTy::Ptr(_), CastTy::Int(_)) |
1387                     (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
1388                         unleash_miri!(self);
1389                         if !self.tcx.features().const_raw_ptr_to_usize_cast
1390                             && !self.suppress_errors
1391                         {
1392                             // in const fn and constants require the feature gate
1393                             // FIXME: make it unsafe inside const fn and constants
1394                             self.record_error(ops::RawPtrToIntCast);
1395                             emit_feature_err(
1396                                 &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
1397                                 self.span, GateIssue::Language,
1398                                 &format!(
1399                                     "casting pointers to integers in {}s is unstable",
1400                                     self.mode,
1401                                 ),
1402                             );
1403                         }
1404                     }
1405                     _ => {}
1406                 }
1407             }
1408
1409             Rvalue::BinaryOp(op, ref lhs, _) => {
1410                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
1411                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
1412                             op == BinOp::Le || op == BinOp::Lt ||
1413                             op == BinOp::Ge || op == BinOp::Gt ||
1414                             op == BinOp::Offset);
1415
1416                     unleash_miri!(self);
1417                     if self.mode.requires_const_checking() &&
1418                         !self.tcx.features().const_compare_raw_pointers &&
1419                         !self.suppress_errors
1420                     {
1421                         self.record_error(ops::RawPtrComparison);
1422                         // require the feature gate inside constants and const fn
1423                         // FIXME: make it unsafe to use these operations
1424                         emit_feature_err(
1425                             &self.tcx.sess.parse_sess,
1426                             sym::const_compare_raw_pointers,
1427                             self.span,
1428                             GateIssue::Language,
1429                             &format!("comparing raw pointers inside {}", self.mode),
1430                         );
1431                     }
1432                 }
1433             }
1434
1435             Rvalue::NullaryOp(NullOp::Box, _) => {
1436                 unleash_miri!(self);
1437                 if self.mode.requires_const_checking() && !self.suppress_errors {
1438                     self.record_error(ops::HeapAllocation);
1439                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1440                                                    "allocations are not allowed in {}s", self.mode);
1441                     err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1442                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
1443                         err.note(
1444                             "The value of statics and constants must be known at compile time, \
1445                              and they live for the entire lifetime of a program. Creating a boxed \
1446                              value allocates memory on the heap at runtime, and therefore cannot \
1447                              be done at compile time."
1448                         );
1449                     }
1450                     err.emit();
1451                 }
1452             }
1453         }
1454     }
1455
1456     fn visit_terminator_kind(&mut self,
1457                              kind: &TerminatorKind<'tcx>,
1458                              location: Location) {
1459         debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
1460         if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1461             if let Some((ref dest, _)) = *destination {
1462                 self.assign(dest, ValueSource::Call {
1463                     callee: func,
1464                     args,
1465                     return_ty: dest.ty(self.body, self.tcx).ty,
1466                 }, location);
1467             }
1468
1469             let fn_ty = func.ty(self.body, self.tcx);
1470             let mut callee_def_id = None;
1471             let mut is_shuffle = false;
1472             match fn_ty.kind {
1473                 ty::FnDef(def_id, _) => {
1474                     callee_def_id = Some(def_id);
1475                     match self.tcx.fn_sig(def_id).abi() {
1476                         Abi::RustIntrinsic |
1477                         Abi::PlatformIntrinsic => {
1478                             assert!(!self.tcx.is_const_fn(def_id));
1479                             match &self.tcx.item_name(def_id).as_str()[..] {
1480                                 // special intrinsic that can be called diretly without an intrinsic
1481                                 // feature gate needs a language feature gate
1482                                 "transmute" => {
1483                                     if self.mode.requires_const_checking()
1484                                         && !self.suppress_errors
1485                                     {
1486                                         // const eval transmute calls only with the feature gate
1487                                         if !self.tcx.features().const_transmute {
1488                                             self.record_error(ops::Transmute);
1489                                             emit_feature_err(
1490                                                 &self.tcx.sess.parse_sess, sym::const_transmute,
1491                                                 self.span, GateIssue::Language,
1492                                                 &format!("The use of std::mem::transmute() \
1493                                                 is gated in {}s", self.mode));
1494                                         }
1495                                     }
1496                                 }
1497
1498                                 name if name.starts_with("simd_shuffle") => {
1499                                     is_shuffle = true;
1500                                 }
1501
1502                                 // no need to check feature gates, intrinsics are only callable
1503                                 // from the libstd or with forever unstable feature gates
1504                                 _ => {}
1505                             }
1506                         }
1507                         _ => {
1508                             // In normal functions no calls are feature-gated.
1509                             if self.mode.requires_const_checking() {
1510                                 let unleash_miri = self
1511                                     .tcx
1512                                     .sess
1513                                     .opts
1514                                     .debugging_opts
1515                                     .unleash_the_miri_inside_of_you;
1516                                 if self.tcx.is_const_fn(def_id)
1517                                     || unleash_miri
1518                                     || self.suppress_errors
1519                                 {
1520                                     // stable const fns or unstable const fns
1521                                     // with their feature gate active
1522                                     // FIXME(eddyb) move stability checks from `is_const_fn` here.
1523                                 } else if self.is_const_panic_fn(def_id) {
1524                                     // Check the const_panic feature gate.
1525                                     // FIXME: cannot allow this inside `allow_internal_unstable`
1526                                     // because that would make `panic!` insta stable in constants,
1527                                     // since the macro is marked with the attribute.
1528                                     if !self.tcx.features().const_panic {
1529                                         // Don't allow panics in constants without the feature gate.
1530                                         self.record_error(ops::Panic);
1531                                         emit_feature_err(
1532                                             &self.tcx.sess.parse_sess,
1533                                             sym::const_panic,
1534                                             self.span,
1535                                             GateIssue::Language,
1536                                             &format!("panicking in {}s is unstable", self.mode),
1537                                         );
1538                                     }
1539                                 } else if let Some(feature)
1540                                               = self.tcx.is_unstable_const_fn(def_id) {
1541                                     // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1542                                     // functions without the feature gate active in this crate in
1543                                     // order to report a better error message than the one below.
1544                                     if !self.span.allows_unstable(feature) {
1545                                         self.record_error(ops::FnCallUnstable(def_id, feature));
1546                                         let mut err = self.tcx.sess.struct_span_err(self.span,
1547                                             &format!("`{}` is not yet stable as a const fn",
1548                                                     self.tcx.def_path_str(def_id)));
1549                                         if nightly_options::is_nightly_build() {
1550                                             help!(&mut err,
1551                                                   "add `#![feature({})]` to the \
1552                                                    crate attributes to enable",
1553                                                   feature);
1554                                         }
1555                                         err.emit();
1556                                     }
1557                                 } else {
1558                                     self.record_error(ops::FnCallNonConst(def_id));
1559                                     let mut err = struct_span_err!(
1560                                         self.tcx.sess,
1561                                         self.span,
1562                                         E0015,
1563                                         "calls in {}s are limited to constant functions, \
1564                                          tuple structs and tuple variants",
1565                                         self.mode,
1566                                     );
1567                                     err.emit();
1568                                 }
1569                             }
1570                         }
1571                     }
1572                 }
1573                 ty::FnPtr(_) => {
1574                     unleash_miri!(self);
1575                     if self.mode.requires_const_checking() && !self.suppress_errors {
1576                         self.record_error(ops::FnCallIndirect);
1577                         let mut err = self.tcx.sess.struct_span_err(
1578                             self.span,
1579                             "function pointers are not allowed in const fn"
1580                         );
1581                         err.emit();
1582                     }
1583                 }
1584                 _ => {
1585                     self.not_const(ops::FnCallOther);
1586                 }
1587             }
1588
1589             // No need to do anything in constants and statics, as everything is "constant" anyway
1590             // so promotion would be useless.
1591             if self.mode != Mode::Static && self.mode != Mode::Const {
1592                 let constant_args = callee_def_id.and_then(|id| {
1593                     args_required_const(self.tcx, id)
1594                 }).unwrap_or_default();
1595                 for (i, arg) in args.iter().enumerate() {
1596                     if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
1597                         continue;
1598                     }
1599
1600                     let candidate = Candidate::Argument { bb: location.block, index: i };
1601                     // Since the argument is required to be constant,
1602                     // we care about constness, not promotability.
1603                     // If we checked for promotability, we'd miss out on
1604                     // the results of function calls (which are never promoted
1605                     // in runtime code).
1606                     // This is not a problem, because the argument explicitly
1607                     // requests constness, in contrast to regular promotion
1608                     // which happens even without the user requesting it.
1609                     //
1610                     // `promote_consts` is responsible for emitting the error if
1611                     // the argument is not promotable.
1612                     if !IsNotPromotable::in_operand(self, arg) {
1613                         debug!("visit_terminator_kind: candidate={:?}", candidate);
1614                         self.promotion_candidates.push(candidate);
1615                     }
1616                 }
1617             }
1618
1619             // Check callee and argument operands.
1620             self.visit_operand(func, location);
1621             for arg in args {
1622                 self.visit_operand(arg, location);
1623             }
1624         } else if let TerminatorKind::Drop {
1625             location: ref place, ..
1626         } | TerminatorKind::DropAndReplace {
1627             location: ref place, ..
1628         } = *kind {
1629             match *kind {
1630                 TerminatorKind::DropAndReplace { .. } => {}
1631                 _ => self.super_terminator_kind(kind, location),
1632             }
1633
1634             // Deny *any* live drops anywhere other than functions.
1635             if self.mode.requires_const_checking() && !self.suppress_errors {
1636                 unleash_miri!(self);
1637                 // HACK(eddyb): emulate a bit of dataflow analysis,
1638                 // conservatively, that drop elaboration will do.
1639                 let needs_drop = if let Some(local) = place.as_local() {
1640                     if NeedsDrop::in_local(self, local) {
1641                         Some(self.body.local_decls[local].source_info.span)
1642                     } else {
1643                         None
1644                     }
1645                 } else {
1646                     Some(self.span)
1647                 };
1648
1649                 if let Some(span) = needs_drop {
1650                     // Double-check the type being dropped, to minimize false positives.
1651                     let ty = place.ty(self.body, self.tcx).ty;
1652                     if ty.needs_drop(self.tcx, self.param_env) {
1653                         self.record_error_spanned(ops::LiveDrop, span);
1654                         struct_span_err!(self.tcx.sess, span, E0493,
1655                                          "destructors cannot be evaluated at compile-time")
1656                             .span_label(span, format!("{}s cannot evaluate destructors",
1657                                                       self.mode))
1658                             .emit();
1659                     }
1660                 }
1661             }
1662
1663             match *kind {
1664                 TerminatorKind::DropAndReplace { ref value, .. } => {
1665                     self.assign(place, ValueSource::DropAndReplace(value), location);
1666                     self.visit_operand(value, location);
1667                 }
1668                 _ => {}
1669             }
1670         } else {
1671             // Qualify any operands inside other terminators.
1672             self.super_terminator_kind(kind, location);
1673         }
1674     }
1675
1676     fn visit_assign(&mut self,
1677                     dest: &Place<'tcx>,
1678                     rvalue: &Rvalue<'tcx>,
1679                     location: Location) {
1680         debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1681         self.assign(dest, ValueSource::Rvalue(rvalue), location);
1682
1683         self.visit_rvalue(rvalue, location);
1684     }
1685
1686     fn visit_source_info(&mut self, source_info: &SourceInfo) {
1687         debug!("visit_source_info: source_info={:?}", source_info);
1688         self.span = source_info.span;
1689     }
1690
1691     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1692         debug!("visit_statement: statement={:?} location={:?}", statement, location);
1693         match statement.kind {
1694             StatementKind::Assign(..) => {
1695                 self.super_statement(statement, location);
1696             }
1697             StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
1698                 self.not_const(ops::IfOrMatch);
1699             }
1700             // FIXME(eddyb) should these really do nothing?
1701             StatementKind::FakeRead(..) |
1702             StatementKind::SetDiscriminant { .. } |
1703             StatementKind::StorageLive(_) |
1704             StatementKind::StorageDead(_) |
1705             StatementKind::InlineAsm {..} |
1706             StatementKind::Retag { .. } |
1707             StatementKind::AscribeUserType(..) |
1708             StatementKind::Nop => {}
1709         }
1710     }
1711 }
1712
1713 pub fn provide(providers: &mut Providers<'_>) {
1714     *providers = Providers {
1715         mir_const_qualif,
1716         ..*providers
1717     };
1718 }
1719
1720 // FIXME(eddyb) this is only left around for the validation logic
1721 // in `promote_consts`, see the comment in `validate_operand`.
1722 pub(super) const QUALIF_ERROR_BIT: u8 = 1 << IsNotPromotable::IDX;
1723
1724 fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
1725     // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1726     // cannot yet be stolen), because `mir_validated()`, which steals
1727     // from `mir_const(), forces this query to execute before
1728     // performing the steal.
1729     let body = &tcx.mir_const(def_id).borrow();
1730
1731     if body.return_ty().references_error() {
1732         tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
1733         return (QUALIF_ERROR_BIT, tcx.arena.alloc(BitSet::new_empty(0)));
1734     }
1735
1736     Checker::new(tcx, def_id, body, Mode::Const).check_const()
1737 }
1738
1739 pub struct QualifyAndPromoteConstants<'tcx> {
1740     pub promoted: Cell<IndexVec<Promoted, Body<'tcx>>>,
1741 }
1742
1743 impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> {
1744     fn default() -> Self {
1745         QualifyAndPromoteConstants {
1746             promoted: Cell::new(IndexVec::new()),
1747         }
1748     }
1749 }
1750
1751 impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
1752     fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
1753         // There's not really any point in promoting errorful MIR.
1754         if body.return_ty().references_error() {
1755             tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
1756             return;
1757         }
1758
1759         if src.promoted.is_some() {
1760             return;
1761         }
1762
1763         let def_id = src.def_id();
1764         let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1765
1766         let mode = determine_mode(tcx, hir_id, def_id);
1767
1768         debug!("run_pass: mode={:?}", mode);
1769         if let Mode::NonConstFn | Mode::ConstFn = mode {
1770             // This is ugly because Checker holds onto mir,
1771             // which can't be mutated until its scope ends.
1772             let (temps, candidates) = {
1773                 let mut checker = Checker::new(tcx, def_id, body, mode);
1774                 if let Mode::ConstFn = mode {
1775                     let use_min_const_fn_checks =
1776                         !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
1777                         tcx.is_min_const_fn(def_id);
1778                     if use_min_const_fn_checks {
1779                         // Enforce `min_const_fn` for stable `const fn`s.
1780                         use super::qualify_min_const_fn::is_min_const_fn;
1781                         if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1782                             error_min_const_fn_violation(tcx, span, err);
1783                             return;
1784                         }
1785
1786                         // `check_const` should not produce any errors, but better safe than sorry
1787                         // FIXME(#53819)
1788                         // NOTE(eddyb) `check_const` is actually needed for promotion inside
1789                         // `min_const_fn` functions.
1790                     }
1791
1792                     // Enforce a constant-like CFG for `const fn`.
1793                     checker.check_const();
1794                 } else {
1795                     while let Some((bb, data)) = checker.rpo.next() {
1796                         checker.visit_basic_block_data(bb, data);
1797                     }
1798                 }
1799
1800                 let promotion_candidates = checker.valid_promotion_candidates();
1801                 (checker.temp_promotion_state, promotion_candidates)
1802             };
1803
1804             // Do the actual promotion, now that we know what's viable.
1805             self.promoted.set(
1806                 promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
1807             );
1808         } else {
1809             check_short_circuiting_in_const_local(tcx, body, mode);
1810
1811             let promoted_temps = match mode {
1812                 Mode::Const => tcx.mir_const_qualif(def_id).1,
1813                 _ => Checker::new(tcx, def_id, body, mode).check_const().1,
1814             };
1815             remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
1816         }
1817
1818         if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
1819             // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
1820             check_static_is_sync(tcx, body, hir_id);
1821         }
1822     }
1823 }
1824
1825 fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
1826     match tcx.hir().body_owner_kind(hir_id) {
1827         hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1828         hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
1829         hir::BodyOwnerKind::Fn => Mode::NonConstFn,
1830         hir::BodyOwnerKind::Const => Mode::Const,
1831         hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1832         hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1833     }
1834 }
1835
1836 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
1837     struct_span_err!(tcx.sess, span, E0723, "{}", msg)
1838         .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
1839         .help("add `#![feature(const_fn)]` to the crate attributes to enable")
1840         .emit();
1841 }
1842
1843 fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
1844     if body.control_flow_destroyed.is_empty() {
1845         return;
1846     }
1847
1848     let mut locals = body.vars_iter();
1849     if let Some(local) = locals.next() {
1850         let span = body.local_decls[local].source_info.span;
1851         let mut error = tcx.sess.struct_span_err(
1852             span,
1853             &format!(
1854                 "new features like let bindings are not permitted in {}s \
1855                 which also use short circuiting operators",
1856                 mode,
1857             ),
1858         );
1859         for (span, kind) in body.control_flow_destroyed.iter() {
1860             error.span_note(
1861                 *span,
1862                 &format!("use of {} here does not actually short circuit due to \
1863                 the const evaluator presently not being able to do control flow. \
1864                 See https://github.com/rust-lang/rust/issues/49146 for more \
1865                 information.", kind),
1866             );
1867         }
1868         for local in locals {
1869             let span = body.local_decls[local].source_info.span;
1870             error.span_note(span, "more locals defined here");
1871         }
1872         error.emit();
1873     }
1874 }
1875
1876 /// In `const` and `static` everything without `StorageDead`
1877 /// is `'static`, we don't have to create promoted MIR fragments,
1878 /// just remove `Drop` and `StorageDead` on "promoted" locals.
1879 fn remove_drop_and_storage_dead_on_promoted_locals(
1880     body: &mut Body<'tcx>,
1881     promoted_temps: &BitSet<Local>,
1882 ) {
1883     debug!("run_pass: promoted_temps={:?}", promoted_temps);
1884
1885     for block in body.basic_blocks_mut() {
1886         block.statements.retain(|statement| {
1887             match statement.kind {
1888                 StatementKind::StorageDead(index) => !promoted_temps.contains(index),
1889                 _ => true
1890             }
1891         });
1892         let terminator = block.terminator_mut();
1893         match &terminator.kind {
1894             TerminatorKind::Drop {
1895                 location,
1896                 target,
1897                 ..
1898             } => {
1899                 if let Some(index) = location.as_local() {
1900                     if promoted_temps.contains(index) {
1901                         terminator.kind = TerminatorKind::Goto { target: *target };
1902                     }
1903                 }
1904             }
1905             _ => {}
1906         }
1907     }
1908 }
1909
1910 fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
1911     let ty = body.return_ty();
1912     tcx.infer_ctxt().enter(|infcx| {
1913         let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1914         let mut fulfillment_cx = traits::FulfillmentContext::new();
1915         let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
1916         fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1917         if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1918             infcx.report_fulfillment_errors(&err, None, false);
1919         }
1920     });
1921 }
1922
1923 fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1924     let attrs = tcx.get_attrs(def_id);
1925     let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
1926     let mut ret = FxHashSet::default();
1927     for meta in attr.meta_item_list()? {
1928         match meta.literal()?.kind {
1929             LitKind::Int(a, _) => { ret.insert(a as usize); }
1930             _ => return None,
1931         }
1932     }
1933     Some(ret)
1934 }
1935
1936 fn validator_mismatch(
1937     tcx: TyCtxt<'tcx>,
1938     body: &Body<'tcx>,
1939     mut old_errors: Vec<(Span, String)>,
1940     mut new_errors: Vec<(Span, String)>,
1941 ) {
1942     error!("old validator: {:?}", old_errors);
1943     error!("new validator: {:?}", new_errors);
1944
1945     // ICE on nightly if the validators do not emit exactly the same errors.
1946     // Users can supress this panic with an unstable compiler flag (hopefully after
1947     // filing an issue).
1948     let opts = &tcx.sess.opts;
1949     let strict_validation_enabled = opts.unstable_features.is_nightly_build()
1950         && !opts.debugging_opts.suppress_const_validation_back_compat_ice;
1951
1952     if !strict_validation_enabled {
1953         return;
1954     }
1955
1956     // If this difference would cause a regression from the old to the new or vice versa, trigger
1957     // the ICE.
1958     if old_errors.is_empty() || new_errors.is_empty() {
1959         span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1960     }
1961
1962     // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the
1963     // new validator to be more conservative about when a dropped local has been moved out of.
1964     //
1965     // Supress the mismatch ICE in cases where the validators disagree only on the number of
1966     // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s.
1967
1968     let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop");
1969     let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow");
1970
1971     let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect();
1972     let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect();
1973
1974     let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors;
1975
1976     let old_mut_borrows = old_errors.iter().filter(is_mut_borrow);
1977     let new_mut_borrows = new_errors.iter().filter(is_mut_borrow);
1978
1979     let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some();
1980
1981     if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) {
1982         return;
1983     }
1984
1985     span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1986 }
1987
1988 const VALIDATOR_MISMATCH_ERR: &str =
1989     r"Disagreement between legacy and dataflow-based const validators.
1990     After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code.";