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