]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/qualify_consts.rs
5ad5363768d34a3142480b24f514020470b14e97
[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_target::spec::abi::Abi;
10 use rustc::hir;
11 use rustc::hir::def_id::DefId;
12 use rustc::traits::{self, TraitEngine};
13 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
14 use rustc::ty::cast::CastTy;
15 use rustc::ty::query::Providers;
16 use rustc::mir::*;
17 use rustc::mir::interpret::ConstValue;
18 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
19 use rustc::middle::lang_items;
20 use rustc::session::config::nightly_options;
21 use syntax::feature_gate::{emit_feature_err, GateIssue};
22 use syntax::symbol::sym;
23 use syntax_pos::{Span, DUMMY_SP};
24
25 use std::borrow::Cow;
26 use std::cell::Cell;
27 use std::fmt;
28 use std::ops::{Deref, Index, IndexMut};
29 use std::usize;
30
31 use rustc::hir::HirId;
32 use crate::transform::{MirPass, MirSource};
33 use crate::transform::check_consts::ops::{self, NonConstOp};
34
35 /// What kind of item we are in.
36 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
37 enum Mode {
38     /// A `static` item.
39     Static,
40     /// A `static mut` item.
41     StaticMut,
42     /// A `const fn` item.
43     ConstFn,
44     /// A `const` item or an anonymous constant (e.g. in array lengths).
45     Const,
46     /// Other type of `fn`.
47     NonConstFn,
48 }
49
50 impl Mode {
51     /// Determine whether we have to do full const-checking because syntactically, we
52     /// are required to be "const".
53     #[inline]
54     fn requires_const_checking(self) -> bool {
55         self != Mode::NonConstFn
56     }
57 }
58
59 impl fmt::Display for Mode {
60     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61         match *self {
62             Mode::Const => write!(f, "constant"),
63             Mode::Static | Mode::StaticMut => write!(f, "static"),
64             Mode::ConstFn => write!(f, "constant function"),
65             Mode::NonConstFn => write!(f, "function")
66         }
67     }
68 }
69
70 const QUALIF_COUNT: usize = 2;
71
72 // FIXME(eddyb) once we can use const generics, replace this array with
73 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
74 #[derive(Copy, Clone, Default)]
75 struct PerQualif<T>([T; QUALIF_COUNT]);
76
77 impl<T: Clone> PerQualif<T> {
78     fn new(x: T) -> Self {
79         PerQualif([x.clone(), x])
80     }
81 }
82
83 impl<T> PerQualif<T> {
84     fn as_mut(&mut self) -> PerQualif<&mut T> {
85         let [x0, x1] = &mut self.0;
86         PerQualif([x0, x1])
87     }
88
89     fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
90         let [x0, x1] = self.0;
91         let [y0, y1] = other.0;
92         PerQualif([(x0, y0), (x1, y1)])
93     }
94 }
95
96 impl PerQualif<bool> {
97     fn encode_to_bits(self) -> u8 {
98         self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
99             bits | ((qualif as u8) << i)
100         })
101     }
102
103     fn decode_from_bits(bits: u8) -> Self {
104         let mut qualifs = Self::default();
105         for (i, qualif) in qualifs.0.iter_mut().enumerate() {
106             *qualif = (bits & (1 << i)) != 0;
107         }
108         qualifs
109     }
110 }
111
112 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
113     type Output = T;
114
115     fn index(&self, _: Q) -> &T {
116         &self.0[Q::IDX]
117     }
118 }
119
120 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
121     fn index_mut(&mut self, _: Q) -> &mut T {
122         &mut self.0[Q::IDX]
123     }
124 }
125
126 struct ConstCx<'a, 'tcx> {
127     tcx: TyCtxt<'tcx>,
128     param_env: ty::ParamEnv<'tcx>,
129     mode: Mode,
130     body: &'a Body<'tcx>,
131
132     per_local: PerQualif<BitSet<Local>>,
133 }
134
135 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
136     fn is_const_panic_fn(&self, def_id: DefId) -> bool {
137         Some(def_id) == self.tcx.lang_items().panic_fn() ||
138         Some(def_id) == self.tcx.lang_items().begin_panic_fn()
139     }
140 }
141
142 #[derive(Copy, Clone, Debug)]
143 enum ValueSource<'a, 'tcx> {
144     Rvalue(&'a Rvalue<'tcx>),
145     DropAndReplace(&'a Operand<'tcx>),
146     Call {
147         callee: &'a Operand<'tcx>,
148         args: &'a [Operand<'tcx>],
149         return_ty: Ty<'tcx>,
150     },
151 }
152
153 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
154 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
155 /// "I found something bad, no reason to go on searching". `false` is only returned if we
156 /// definitely cannot find anything bad anywhere.
157 ///
158 /// The default implementations proceed structurally.
159 trait Qualif {
160     const IDX: usize;
161
162     /// Return the qualification that is (conservatively) correct for any value
163     /// of the type, or `None` if the qualification is not value/type-based.
164     fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
165         None
166     }
167
168     /// Return a mask for the qualification, given a type. This is `false` iff
169     /// no value of that type can have the qualification.
170     fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
171         Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
172     }
173
174     fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
175         cx.per_local.0[Self::IDX].contains(local)
176     }
177
178     fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
179         // FIXME(eddyb) should we do anything here for value properties?
180         false
181     }
182
183     fn in_projection_structurally(
184         cx: &ConstCx<'_, 'tcx>,
185         place: PlaceRef<'_, 'tcx>,
186     ) -> bool {
187         if let [proj_base @ .., elem] = place.projection {
188             let base_qualif = Self::in_place(cx, PlaceRef {
189                 base: place.base,
190                 projection: proj_base,
191             });
192             let qualif = base_qualif && Self::mask_for_ty(
193                 cx,
194                 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
195                     .projection_ty(cx.tcx, elem)
196                     .ty,
197             );
198             match elem {
199                 ProjectionElem::Deref |
200                 ProjectionElem::Subslice { .. } |
201                 ProjectionElem::Field(..) |
202                 ProjectionElem::ConstantIndex { .. } |
203                 ProjectionElem::Downcast(..) => qualif,
204
205                 // FIXME(eddyb) shouldn't this be masked *after* including the
206                 // index local? Then again, it's `usize` which is neither
207                 // `HasMutInterior` nor `NeedsDrop`.
208                 ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
209             }
210         } else {
211             bug!("This should be called if projection is not empty");
212         }
213     }
214
215     fn in_projection(
216         cx: &ConstCx<'_, 'tcx>,
217         place: PlaceRef<'_, 'tcx>,
218     ) -> bool {
219         Self::in_projection_structurally(cx, place)
220     }
221
222     fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
223         match place {
224             PlaceRef {
225                 base: PlaceBase::Local(local),
226                 projection: [],
227             } => Self::in_local(cx, *local),
228             PlaceRef {
229                 base: PlaceBase::Static(box Static {
230                     kind: StaticKind::Promoted(..),
231                     ..
232                 }),
233                 projection: [],
234             } => bug!("qualifying already promoted MIR"),
235             PlaceRef {
236                 base: PlaceBase::Static(static_),
237                 projection: [],
238             } => {
239                 Self::in_static(cx, static_)
240             },
241             PlaceRef {
242                 base: _,
243                 projection: [.., _],
244             } => Self::in_projection(cx, place),
245         }
246     }
247
248     fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
249         match *operand {
250             Operand::Copy(ref place) |
251             Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
252
253             Operand::Constant(ref constant) => {
254                 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
255                     // Don't peek inside trait associated constants.
256                     if cx.tcx.trait_of_item(def_id).is_some() {
257                         Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
258                     } else {
259                         let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
260
261                         let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
262
263                         // Just in case the type is more specific than
264                         // the definition, e.g., impl associated const
265                         // with type parameters, take it into account.
266                         qualif && Self::mask_for_ty(cx, constant.literal.ty)
267                     }
268                 } else {
269                     false
270                 }
271             }
272         }
273     }
274
275     fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
276         match *rvalue {
277             Rvalue::NullaryOp(..) => false,
278
279             Rvalue::Discriminant(ref place) |
280             Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
281
282             Rvalue::Use(ref operand) |
283             Rvalue::Repeat(ref operand, _) |
284             Rvalue::UnaryOp(_, ref operand) |
285             Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
286
287             Rvalue::BinaryOp(_, ref lhs, ref rhs) |
288             Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
289                 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
290             }
291
292             Rvalue::Ref(_, _, ref place) => {
293                 // Special-case reborrows to be more like a copy of the reference.
294                 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
295                     if ProjectionElem::Deref == elem {
296                         let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
297                         if let ty::Ref(..) = base_ty.kind {
298                             return Self::in_place(cx, PlaceRef {
299                                 base: &place.base,
300                                 projection: proj_base,
301                             });
302                         }
303                     }
304                 }
305
306                 Self::in_place(cx, place.as_ref())
307             }
308
309             Rvalue::Aggregate(_, ref operands) => {
310                 operands.iter().any(|o| Self::in_operand(cx, o))
311             }
312         }
313     }
314
315     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
316         Self::in_rvalue_structurally(cx, rvalue)
317     }
318
319     fn in_call(
320         cx: &ConstCx<'_, 'tcx>,
321         _callee: &Operand<'tcx>,
322         _args: &[Operand<'tcx>],
323         return_ty: Ty<'tcx>,
324     ) -> bool {
325         // Be conservative about the returned value of a const fn.
326         Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
327     }
328
329     fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
330         match source {
331             ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
332             ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
333             ValueSource::Call { callee, args, return_ty } => {
334                 Self::in_call(cx, callee, args, return_ty)
335             }
336         }
337     }
338 }
339
340 /// Constant containing interior mutability (`UnsafeCell<T>`).
341 /// This must be ruled out to make sure that evaluating the constant at compile-time
342 /// and at *any point* during the run-time would produce the same result. In particular,
343 /// promotion of temporaries must not change program behavior; if the promoted could be
344 /// written to, that would be a problem.
345 struct HasMutInterior;
346
347 impl Qualif for HasMutInterior {
348     const IDX: usize = 0;
349
350     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
351         Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
352     }
353
354     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
355         match *rvalue {
356             // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
357             // allowed in constants (and the `Checker` will error), and/or it
358             // won't be promoted, due to `&mut ...` or interior mutability.
359             Rvalue::Ref(_, kind, ref place) => {
360                 let ty = place.ty(cx.body, cx.tcx).ty;
361
362                 if let BorrowKind::Mut { .. } = kind {
363                     // In theory, any zero-sized value could be borrowed
364                     // mutably without consequences. However, only &mut []
365                     // is allowed right now, and only in functions.
366                     if cx.mode == Mode::StaticMut {
367                         // Inside a `static mut`, &mut [...] is also allowed.
368                         match ty.kind {
369                             ty::Array(..) | ty::Slice(_) => {}
370                             _ => return true,
371                         }
372                     } else if let ty::Array(_, len) = ty.kind {
373                         // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
374                         // seems unnecessary, given that this is merely a ZST.
375                         match len.try_eval_usize(cx.tcx, cx.param_env) {
376                             Some(0) if cx.mode == Mode::NonConstFn => {},
377                             _ => return true,
378                         }
379                     } else {
380                         return true;
381                     }
382                 }
383             }
384
385             Rvalue::Aggregate(ref kind, _) => {
386                 if let AggregateKind::Adt(def, ..) = **kind {
387                     if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
388                         let ty = rvalue.ty(cx.body, cx.tcx);
389                         assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
390                         return true;
391                     }
392                 }
393             }
394
395             _ => {}
396         }
397
398         Self::in_rvalue_structurally(cx, rvalue)
399     }
400 }
401
402 /// Constant containing an ADT that implements `Drop`.
403 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
404 /// as that might not be a `const fn`, and (b) because implicit promotion would
405 /// remove side-effects that occur as part of dropping that value.
406 struct NeedsDrop;
407
408 impl Qualif for NeedsDrop {
409     const IDX: usize = 1;
410
411     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
412         Some(ty.needs_drop(cx.tcx, cx.param_env))
413     }
414
415     fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
416         if let Rvalue::Aggregate(ref kind, _) = *rvalue {
417             if let AggregateKind::Adt(def, ..) = **kind {
418                 if def.has_dtor(cx.tcx) {
419                     return true;
420                 }
421             }
422         }
423
424         Self::in_rvalue_structurally(cx, rvalue)
425     }
426 }
427
428 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
429 macro_rules! static_assert_seq_qualifs {
430     ($i:expr => $first:ident $(, $rest:ident)*) => {
431         static_assert!({
432             static_assert_seq_qualifs!($i + 1 => $($rest),*);
433
434             $first::IDX == $i
435         });
436     };
437     ($i:expr =>) => {
438         static_assert!(QUALIF_COUNT == $i);
439     };
440 }
441 static_assert_seq_qualifs!(
442     0 => HasMutInterior, NeedsDrop
443 );
444
445 impl ConstCx<'_, 'tcx> {
446     fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
447         let mut qualifs = PerQualif::default();
448         qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
449         qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
450         qualifs
451     }
452
453     fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
454         let mut qualifs = PerQualif::default();
455         qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
456         qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
457         qualifs
458     }
459
460     fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
461         let mut qualifs = PerQualif::default();
462         qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
463         qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
464         qualifs
465     }
466 }
467
468 /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
469 /// for value qualifications, and accumulates writes of
470 /// rvalue/call results to locals, in `local_qualif`.
471 /// It also records candidates for promotion in `promotion_candidates`,
472 /// both in functions and const/static items.
473 struct Checker<'a, 'tcx> {
474     cx: ConstCx<'a, 'tcx>,
475
476     span: Span,
477     def_id: DefId,
478
479     /// If `true`, do not emit errors to the user, merely collect them in `errors`.
480     suppress_errors: bool,
481     errors: Vec<(Span, String)>,
482 }
483
484 macro_rules! unleash_miri {
485     ($this:expr) => {{
486         if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
487             if $this.mode.requires_const_checking() && !$this.suppress_errors {
488                 $this.tcx.sess.span_warn($this.span, "skipping const checks");
489             }
490             return;
491         }
492     }}
493 }
494
495 impl Deref for Checker<'a, 'tcx> {
496     type Target = ConstCx<'a, 'tcx>;
497
498     fn deref(&self) -> &Self::Target {
499         &self.cx
500     }
501 }
502
503 impl<'a, 'tcx> Checker<'a, 'tcx> {
504     fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
505         assert!(def_id.is_local());
506
507         let param_env = tcx.param_env(def_id);
508
509         let mut cx = ConstCx {
510             tcx,
511             param_env,
512             mode,
513             body,
514             per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
515         };
516
517         for (local, decl) in body.local_decls.iter_enumerated() {
518             if let LocalKind::Arg = body.local_kind(local) {
519                 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
520                 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
521                     if *qualif {
522                         per_local.insert(local);
523                     }
524                 }
525             }
526         }
527
528         Checker {
529             cx,
530             span: body.span,
531             def_id,
532             errors: vec![],
533             suppress_errors: false,
534         }
535     }
536
537     // FIXME(eddyb) we could split the errors into meaningful
538     // categories, but enabling full miri would make that
539     // slightly pointless (even with feature-gating).
540     fn not_const(&mut self, op: impl NonConstOp) {
541         unleash_miri!(self);
542         if self.mode.requires_const_checking() && !self.suppress_errors {
543             self.record_error(op);
544             let mut err = struct_span_err!(
545                 self.tcx.sess,
546                 self.span,
547                 E0019,
548                 "{} contains unimplemented expression type",
549                 self.mode
550             );
551             if self.tcx.sess.teach(&err.get_code().unwrap()) {
552                 err.note("A function call isn't allowed in the const's initialization expression \
553                           because the expression's value must be known at compile-time.");
554                 err.note("Remember: you can't use a function call inside a const's initialization \
555                           expression! However, you can use it anywhere else.");
556             }
557             err.emit();
558         }
559     }
560
561     fn record_error(&mut self, op: impl NonConstOp) {
562         self.record_error_spanned(op, self.span);
563     }
564
565     fn record_error_spanned(&mut self, op: impl NonConstOp, span: Span) {
566         self.errors.push((span, format!("{:?}", op)));
567     }
568
569     /// Assigns an rvalue/call qualification to the given destination.
570     fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
571         trace!("assign: {:?} <- {:?}", dest, source);
572
573         let mut qualifs = self.qualifs_in_value(source);
574
575         match source {
576             ValueSource::Rvalue(&Rvalue::Ref(_, kind, _)) => {
577                 // Getting `true` from `HasMutInterior::in_rvalue` means
578                 // the borrowed place is disallowed from being borrowed,
579                 // due to either a mutable borrow (with some exceptions),
580                 // or an shared borrow of a value with interior mutability.
581                 // Then `HasMutInterior` is cleared
582                 // to avoid duplicate errors (e.g. from reborrowing).
583                 if qualifs[HasMutInterior] {
584                     qualifs[HasMutInterior] = false;
585
586                     debug!("suppress_errors: {}", self.suppress_errors);
587                     if self.mode.requires_const_checking() && !self.suppress_errors {
588                         if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
589                             self.record_error(ops::MutBorrow(kind));
590                             if let BorrowKind::Mut { .. } = kind {
591                                 let mut err = struct_span_err!(self.tcx.sess,  self.span, E0017,
592                                                                "references in {}s may only refer \
593                                                                 to immutable values", self.mode);
594                                 err.span_label(self.span, format!("{}s require immutable values",
595                                                                     self.mode));
596                                 if self.tcx.sess.teach(&err.get_code().unwrap()) {
597                                     err.note("References in statics and constants may only refer \
598                                               to immutable values.\n\n\
599                                               Statics are shared everywhere, and if they refer to \
600                                               mutable data one might violate memory safety since \
601                                               holding multiple mutable references to shared data \
602                                               is not allowed.\n\n\
603                                               If you really want global mutable state, try using \
604                                               static mut or a global UnsafeCell.");
605                                 }
606                                 err.emit();
607                             } else {
608                                 span_err!(self.tcx.sess, self.span, E0492,
609                                           "cannot borrow a constant which may contain \
610                                            interior mutability, create a static instead");
611                             }
612                         }
613                     }
614                 }
615             },
616             _ => {},
617         }
618
619         let mut dest_projection = &dest.projection[..];
620         let index = loop {
621             match (&dest.base, dest_projection) {
622                 // We treat all locals equal in constants
623                 (&PlaceBase::Local(index), []) => break index,
624                 // projections are transparent for assignments
625                 // we qualify the entire destination at once, even if just a field would have
626                 // stricter qualification
627                 (base, [proj_base @ .., _]) => {
628                     // Catch more errors in the destination. `visit_place` also checks various
629                     // projection rules like union field access and raw pointer deref
630                     let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
631                     self.visit_place_base(base, context, location);
632                     self.visit_projection(base, dest_projection, context, location);
633                     dest_projection = proj_base;
634                 },
635                 (&PlaceBase::Static(box Static {
636                     kind: StaticKind::Promoted(..),
637                     ..
638                 }), []) => bug!("promoteds don't exist yet during promotion"),
639                 (&PlaceBase::Static(box Static{ kind: _, .. }), []) => {
640                     // Catch more errors in the destination. `visit_place` also checks that we
641                     // do not try to access statics from constants or try to mutate statics
642                     let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
643                     self.visit_place_base(&dest.base, context, location);
644                     return;
645                 }
646             }
647         };
648
649         let kind = self.body.local_kind(index);
650         debug!("store to {:?} {:?}", kind, index);
651
652         // this is overly restrictive, because even full assignments do not clear the qualif
653         // While we could special case full assignments, this would be inconsistent with
654         // aggregates where we overwrite all fields via assignments, which would not get
655         // that feature.
656         for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
657             if *qualif {
658                 per_local.insert(index);
659             }
660         }
661     }
662
663     /// Check a whole const, static initializer or const fn.
664     fn check_const(&mut self) -> u8 {
665         use crate::transform::check_consts as new_checker;
666
667         debug!("const-checking {} {:?}", self.mode, self.def_id);
668
669         // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are
670         // enabled.
671         let use_new_validator = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
672         if use_new_validator {
673             debug!("Using dataflow-based const validator");
674         }
675
676         let item = new_checker::Item::new(self.tcx, self.def_id, self.body);
677         let mut validator = new_checker::validation::Validator::new(&item);
678
679         validator.suppress_errors = !use_new_validator;
680         self.suppress_errors = use_new_validator;
681
682         let body = self.body;
683
684         let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
685         let mut bb = START_BLOCK;
686         loop {
687             seen_blocks.insert(bb.index());
688
689             self.visit_basic_block_data(bb, &body[bb]);
690             validator.visit_basic_block_data(bb, &body[bb]);
691
692             let target = match body[bb].terminator().kind {
693                 TerminatorKind::Goto { target } |
694                 TerminatorKind::FalseUnwind { real_target: target, .. } |
695                 TerminatorKind::Drop { target, .. } |
696                 TerminatorKind::DropAndReplace { target, .. } |
697                 TerminatorKind::Assert { target, .. } |
698                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
699                     Some(target)
700                 }
701
702                 // Non-terminating calls cannot produce any value.
703                 TerminatorKind::Call { destination: None, .. } => {
704                     break;
705                 }
706
707                 TerminatorKind::SwitchInt {..} |
708                 TerminatorKind::Resume |
709                 TerminatorKind::Abort |
710                 TerminatorKind::GeneratorDrop |
711                 TerminatorKind::Yield { .. } |
712                 TerminatorKind::Unreachable |
713                 TerminatorKind::FalseEdges { .. } => None,
714
715                 TerminatorKind::Return => {
716                     break;
717                 }
718             };
719
720             match target {
721                 // No loops allowed.
722                 Some(target) if !seen_blocks.contains(target.index()) => {
723                     bb = target;
724                 }
725                 _ => {
726                     self.not_const(ops::Loop);
727                     validator.check_op(ops::Loop);
728                     break;
729                 }
730             }
731         }
732
733         // The new validation pass should agree with the old when running on simple const bodies
734         // (e.g. no `if` or `loop`).
735         if !use_new_validator {
736             let mut new_errors = validator.take_errors();
737
738             // FIXME: each checker sometimes emits the same error with the same span twice in a row.
739             self.errors.dedup();
740             new_errors.dedup();
741
742             if self.errors != new_errors {
743                 validator_mismatch(
744                     self.tcx,
745                     body,
746                     std::mem::replace(&mut self.errors, vec![]),
747                     new_errors,
748                 );
749             }
750         }
751
752         self.qualifs_in_local(RETURN_PLACE).encode_to_bits()
753     }
754 }
755
756 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
757     fn visit_place_base(
758         &mut self,
759         place_base: &PlaceBase<'tcx>,
760         context: PlaceContext,
761         location: Location,
762     ) {
763         self.super_place_base(place_base, context, location);
764         match place_base {
765             PlaceBase::Local(_) => {}
766             PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
767                 unreachable!()
768             }
769             PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
770                 if self.tcx
771                         .get_attrs(*def_id)
772                         .iter()
773                         .any(|attr| attr.check_name(sym::thread_local)) {
774                     if self.mode.requires_const_checking() && !self.suppress_errors {
775                         self.record_error(ops::ThreadLocalAccess);
776                         span_err!(self.tcx.sess, self.span, E0625,
777                                     "thread-local statics cannot be \
778                                     accessed at compile-time");
779                     }
780                     return;
781                 }
782
783                 // Only allow statics (not consts) to refer to other statics.
784                 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
785                     if self.mode == Mode::Static
786                         && context.is_mutating_use()
787                         && !self.suppress_errors
788                     {
789                         // this is not strictly necessary as miri will also bail out
790                         // For interior mutability we can't really catch this statically as that
791                         // goes through raw pointers and intermediate temporaries, so miri has
792                         // to catch this anyway
793                         self.tcx.sess.span_err(
794                             self.span,
795                             "cannot mutate statics in the initializer of another static",
796                         );
797                     }
798                     return;
799                 }
800                 unleash_miri!(self);
801
802                 if self.mode.requires_const_checking() && !self.suppress_errors {
803                     self.record_error(ops::StaticAccess);
804                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
805                                                     "{}s cannot refer to statics, use \
806                                                     a constant instead", self.mode);
807                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
808                         err.note(
809                             "Static and const variables can refer to other const variables. \
810                                 But a const variable cannot refer to a static variable."
811                         );
812                         err.help(
813                             "To fix this, the value can be extracted as a const and then used."
814                         );
815                     }
816                     err.emit()
817                 }
818             }
819         }
820     }
821
822     fn visit_projection_elem(
823         &mut self,
824         place_base: &PlaceBase<'tcx>,
825         proj_base: &[PlaceElem<'tcx>],
826         elem: &PlaceElem<'tcx>,
827         context: PlaceContext,
828         location: Location,
829     ) {
830         debug!(
831             "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \
832             context={:?} location={:?}",
833             place_base,
834             proj_base,
835             elem,
836             context,
837             location,
838         );
839
840         self.super_projection_elem(place_base, proj_base, elem, context, location);
841
842         match elem {
843             ProjectionElem::Deref => {
844                 if context.is_mutating_use() {
845                     // `not_const` errors out in const contexts
846                     self.not_const(ops::MutDeref)
847                 }
848                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
849                 match self.mode {
850                     Mode::NonConstFn => {}
851                     _ if self.suppress_errors => {}
852                     _ => {
853                         if let ty::RawPtr(_) = base_ty.kind {
854                             if !self.tcx.features().const_raw_ptr_deref {
855                                 self.record_error(ops::RawPtrDeref);
856                                 emit_feature_err(
857                                     &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
858                                     self.span, GateIssue::Language,
859                                     &format!(
860                                         "dereferencing raw pointers in {}s is unstable",
861                                         self.mode,
862                                     ),
863                                 );
864                             }
865                         }
866                     }
867                 }
868             }
869
870             ProjectionElem::ConstantIndex {..} |
871             ProjectionElem::Subslice {..} |
872             ProjectionElem::Field(..) |
873             ProjectionElem::Index(_) => {
874                 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
875                 if let Some(def) = base_ty.ty_adt_def() {
876                     if def.is_union() {
877                         match self.mode {
878                             Mode::ConstFn => {
879                                 if !self.tcx.features().const_fn_union
880                                     && !self.suppress_errors
881                                 {
882                                     self.record_error(ops::UnionAccess);
883                                     emit_feature_err(
884                                         &self.tcx.sess.parse_sess, sym::const_fn_union,
885                                         self.span, GateIssue::Language,
886                                         "unions in const fn are unstable",
887                                     );
888                                 }
889                             },
890
891                             | Mode::NonConstFn
892                             | Mode::Static
893                             | Mode::StaticMut
894                             | Mode::Const
895                             => {},
896                         }
897                     }
898                 }
899             }
900
901             ProjectionElem::Downcast(..) => {
902                 self.not_const(ops::Downcast)
903             }
904         }
905     }
906
907     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
908         debug!("visit_operand: operand={:?} location={:?}", operand, location);
909         self.super_operand(operand, location);
910
911         match *operand {
912             Operand::Move(ref place) => {
913                 // Mark the consumed locals to indicate later drops are noops.
914                 if let Some(local) = place.as_local() {
915                     self.cx.per_local[NeedsDrop].remove(local);
916                 }
917             }
918             Operand::Copy(_) |
919             Operand::Constant(_) => {}
920         }
921     }
922
923     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
924         debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
925
926         // Check nested operands and places.
927         if let Rvalue::Ref(_, kind, ref place) = *rvalue {
928             // Special-case reborrows.
929             let mut reborrow_place = None;
930             if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
931                 if elem == ProjectionElem::Deref {
932                     let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
933                     if let ty::Ref(..) = base_ty.kind {
934                         reborrow_place = Some(proj_base);
935                     }
936                 }
937             }
938
939             if let Some(proj) = reborrow_place {
940                 let ctx = match kind {
941                     BorrowKind::Shared => PlaceContext::NonMutatingUse(
942                         NonMutatingUseContext::SharedBorrow,
943                     ),
944                     BorrowKind::Shallow => PlaceContext::NonMutatingUse(
945                         NonMutatingUseContext::ShallowBorrow,
946                     ),
947                     BorrowKind::Unique => PlaceContext::NonMutatingUse(
948                         NonMutatingUseContext::UniqueBorrow,
949                     ),
950                     BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
951                         MutatingUseContext::Borrow,
952                     ),
953                 };
954                 self.visit_place_base(&place.base, ctx, location);
955                 self.visit_projection(&place.base, proj, ctx, location);
956             } else {
957                 self.super_rvalue(rvalue, location);
958             }
959         } else {
960             self.super_rvalue(rvalue, location);
961         }
962
963         match *rvalue {
964             Rvalue::Use(_) |
965             Rvalue::Repeat(..) |
966             Rvalue::UnaryOp(UnOp::Neg, _) |
967             Rvalue::UnaryOp(UnOp::Not, _) |
968             Rvalue::NullaryOp(NullOp::SizeOf, _) |
969             Rvalue::CheckedBinaryOp(..) |
970             Rvalue::Cast(CastKind::Pointer(_), ..) |
971             Rvalue::Discriminant(..) |
972             Rvalue::Len(_) |
973             Rvalue::Ref(..) |
974             Rvalue::Aggregate(..) => {}
975
976             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
977                 let operand_ty = operand.ty(self.body, self.tcx);
978                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
979                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
980                 match (cast_in, cast_out) {
981                     (CastTy::Ptr(_), CastTy::Int(_)) |
982                     (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
983                         unleash_miri!(self);
984                         if !self.tcx.features().const_raw_ptr_to_usize_cast
985                             && !self.suppress_errors
986                         {
987                             // in const fn and constants require the feature gate
988                             // FIXME: make it unsafe inside const fn and constants
989                             self.record_error(ops::RawPtrToIntCast);
990                             emit_feature_err(
991                                 &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
992                                 self.span, GateIssue::Language,
993                                 &format!(
994                                     "casting pointers to integers in {}s is unstable",
995                                     self.mode,
996                                 ),
997                             );
998                         }
999                     }
1000                     _ => {}
1001                 }
1002             }
1003
1004             Rvalue::BinaryOp(op, ref lhs, _) => {
1005                 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
1006                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
1007                             op == BinOp::Le || op == BinOp::Lt ||
1008                             op == BinOp::Ge || op == BinOp::Gt ||
1009                             op == BinOp::Offset);
1010
1011                     unleash_miri!(self);
1012                     if self.mode.requires_const_checking() &&
1013                         !self.tcx.features().const_compare_raw_pointers &&
1014                         !self.suppress_errors
1015                     {
1016                         self.record_error(ops::RawPtrComparison);
1017                         // require the feature gate inside constants and const fn
1018                         // FIXME: make it unsafe to use these operations
1019                         emit_feature_err(
1020                             &self.tcx.sess.parse_sess,
1021                             sym::const_compare_raw_pointers,
1022                             self.span,
1023                             GateIssue::Language,
1024                             &format!("comparing raw pointers inside {}", self.mode),
1025                         );
1026                     }
1027                 }
1028             }
1029
1030             Rvalue::NullaryOp(NullOp::Box, _) => {
1031                 unleash_miri!(self);
1032                 if self.mode.requires_const_checking() && !self.suppress_errors {
1033                     self.record_error(ops::HeapAllocation);
1034                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1035                                                    "allocations are not allowed in {}s", self.mode);
1036                     err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1037                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
1038                         err.note(
1039                             "The value of statics and constants must be known at compile time, \
1040                              and they live for the entire lifetime of a program. Creating a boxed \
1041                              value allocates memory on the heap at runtime, and therefore cannot \
1042                              be done at compile time."
1043                         );
1044                     }
1045                     err.emit();
1046                 }
1047             }
1048         }
1049     }
1050
1051     fn visit_terminator_kind(&mut self,
1052                              kind: &TerminatorKind<'tcx>,
1053                              location: Location) {
1054         debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
1055         if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1056             if let Some((ref dest, _)) = *destination {
1057                 self.assign(dest, ValueSource::Call {
1058                     callee: func,
1059                     args,
1060                     return_ty: dest.ty(self.body, self.tcx).ty,
1061                 }, location);
1062             }
1063
1064             let fn_ty = func.ty(self.body, self.tcx);
1065             match fn_ty.kind {
1066                 ty::FnDef(def_id, _) => {
1067                     match self.tcx.fn_sig(def_id).abi() {
1068                         Abi::RustIntrinsic |
1069                         Abi::PlatformIntrinsic => {
1070                             assert!(!self.tcx.is_const_fn(def_id));
1071                             match &*self.tcx.item_name(def_id).as_str() {
1072                                 // special intrinsic that can be called diretly without an intrinsic
1073                                 // feature gate needs a language feature gate
1074                                 "transmute" => {
1075                                     if self.mode.requires_const_checking()
1076                                         && !self.suppress_errors
1077                                     {
1078                                         // const eval transmute calls only with the feature gate
1079                                         if !self.tcx.features().const_transmute {
1080                                             self.record_error(ops::Transmute);
1081                                             emit_feature_err(
1082                                                 &self.tcx.sess.parse_sess, sym::const_transmute,
1083                                                 self.span, GateIssue::Language,
1084                                                 &format!("The use of std::mem::transmute() \
1085                                                 is gated in {}s", self.mode));
1086                                         }
1087                                     }
1088                                 }
1089
1090                                 // no need to check feature gates, intrinsics are only callable
1091                                 // from the libstd or with forever unstable feature gates
1092                                 _ => {}
1093                             }
1094                         }
1095                         _ => {
1096                             // In normal functions no calls are feature-gated.
1097                             if self.mode.requires_const_checking() {
1098                                 let unleash_miri = self
1099                                     .tcx
1100                                     .sess
1101                                     .opts
1102                                     .debugging_opts
1103                                     .unleash_the_miri_inside_of_you;
1104                                 if self.tcx.is_const_fn(def_id)
1105                                     || unleash_miri
1106                                     || self.suppress_errors
1107                                 {
1108                                     // stable const fns or unstable const fns
1109                                     // with their feature gate active
1110                                     // FIXME(eddyb) move stability checks from `is_const_fn` here.
1111                                 } else if self.is_const_panic_fn(def_id) {
1112                                     // Check the const_panic feature gate.
1113                                     // FIXME: cannot allow this inside `allow_internal_unstable`
1114                                     // because that would make `panic!` insta stable in constants,
1115                                     // since the macro is marked with the attribute.
1116                                     if !self.tcx.features().const_panic {
1117                                         // Don't allow panics in constants without the feature gate.
1118                                         self.record_error(ops::Panic);
1119                                         emit_feature_err(
1120                                             &self.tcx.sess.parse_sess,
1121                                             sym::const_panic,
1122                                             self.span,
1123                                             GateIssue::Language,
1124                                             &format!("panicking in {}s is unstable", self.mode),
1125                                         );
1126                                     }
1127                                 } else if let Some(feature)
1128                                               = self.tcx.is_unstable_const_fn(def_id) {
1129                                     // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1130                                     // functions without the feature gate active in this crate in
1131                                     // order to report a better error message than the one below.
1132                                     if !self.span.allows_unstable(feature) {
1133                                         self.record_error(ops::FnCallUnstable(def_id, feature));
1134                                         let mut err = self.tcx.sess.struct_span_err(self.span,
1135                                             &format!("`{}` is not yet stable as a const fn",
1136                                                     self.tcx.def_path_str(def_id)));
1137                                         if nightly_options::is_nightly_build() {
1138                                             help!(&mut err,
1139                                                   "add `#![feature({})]` to the \
1140                                                    crate attributes to enable",
1141                                                   feature);
1142                                         }
1143                                         err.emit();
1144                                     }
1145                                 } else {
1146                                     self.record_error(ops::FnCallNonConst(def_id));
1147                                     let mut err = struct_span_err!(
1148                                         self.tcx.sess,
1149                                         self.span,
1150                                         E0015,
1151                                         "calls in {}s are limited to constant functions, \
1152                                          tuple structs and tuple variants",
1153                                         self.mode,
1154                                     );
1155                                     err.emit();
1156                                 }
1157                             }
1158                         }
1159                     }
1160                 }
1161                 ty::FnPtr(_) => {
1162                     unleash_miri!(self);
1163                     if self.mode.requires_const_checking() && !self.suppress_errors {
1164                         self.record_error(ops::FnCallIndirect);
1165                         let mut err = self.tcx.sess.struct_span_err(
1166                             self.span,
1167                             "function pointers are not allowed in const fn"
1168                         );
1169                         err.emit();
1170                     }
1171                 }
1172                 _ => {
1173                     self.not_const(ops::FnCallOther);
1174                 }
1175             }
1176
1177             // Check callee and argument operands.
1178             self.visit_operand(func, location);
1179             for arg in args {
1180                 self.visit_operand(arg, location);
1181             }
1182         } else if let TerminatorKind::Drop {
1183             location: ref place, ..
1184         } | TerminatorKind::DropAndReplace {
1185             location: ref place, ..
1186         } = *kind {
1187             match *kind {
1188                 TerminatorKind::DropAndReplace { .. } => {}
1189                 _ => self.super_terminator_kind(kind, location),
1190             }
1191
1192             // Deny *any* live drops anywhere other than functions.
1193             if self.mode.requires_const_checking() && !self.suppress_errors {
1194                 unleash_miri!(self);
1195                 // HACK(eddyb): emulate a bit of dataflow analysis,
1196                 // conservatively, that drop elaboration will do.
1197                 let needs_drop = if let Some(local) = place.as_local() {
1198                     if NeedsDrop::in_local(self, local) {
1199                         Some(self.body.local_decls[local].source_info.span)
1200                     } else {
1201                         None
1202                     }
1203                 } else {
1204                     Some(self.span)
1205                 };
1206
1207                 if let Some(span) = needs_drop {
1208                     // Double-check the type being dropped, to minimize false positives.
1209                     let ty = place.ty(self.body, self.tcx).ty;
1210                     if ty.needs_drop(self.tcx, self.param_env) {
1211                         self.record_error_spanned(ops::LiveDrop, span);
1212                         struct_span_err!(self.tcx.sess, span, E0493,
1213                                          "destructors cannot be evaluated at compile-time")
1214                             .span_label(span, format!("{}s cannot evaluate destructors",
1215                                                       self.mode))
1216                             .emit();
1217                     }
1218                 }
1219             }
1220
1221             match *kind {
1222                 TerminatorKind::DropAndReplace { ref value, .. } => {
1223                     self.assign(place, ValueSource::DropAndReplace(value), location);
1224                     self.visit_operand(value, location);
1225                 }
1226                 _ => {}
1227             }
1228         } else {
1229             // Qualify any operands inside other terminators.
1230             self.super_terminator_kind(kind, location);
1231         }
1232     }
1233
1234     fn visit_assign(&mut self,
1235                     dest: &Place<'tcx>,
1236                     rvalue: &Rvalue<'tcx>,
1237                     location: Location) {
1238         debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1239         self.assign(dest, ValueSource::Rvalue(rvalue), location);
1240
1241         self.visit_rvalue(rvalue, location);
1242     }
1243
1244     fn visit_source_info(&mut self, source_info: &SourceInfo) {
1245         debug!("visit_source_info: source_info={:?}", source_info);
1246         self.span = source_info.span;
1247     }
1248
1249     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1250         debug!("visit_statement: statement={:?} location={:?}", statement, location);
1251         match statement.kind {
1252             StatementKind::Assign(..) => {
1253                 self.super_statement(statement, location);
1254             }
1255             StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
1256                 self.not_const(ops::IfOrMatch);
1257             }
1258             // FIXME(eddyb) should these really do nothing?
1259             StatementKind::FakeRead(..) |
1260             StatementKind::SetDiscriminant { .. } |
1261             StatementKind::StorageLive(_) |
1262             StatementKind::StorageDead(_) |
1263             StatementKind::InlineAsm {..} |
1264             StatementKind::Retag { .. } |
1265             StatementKind::AscribeUserType(..) |
1266             StatementKind::Nop => {}
1267         }
1268     }
1269 }
1270
1271 pub fn provide(providers: &mut Providers<'_>) {
1272     *providers = Providers {
1273         mir_const_qualif,
1274         ..*providers
1275     };
1276 }
1277
1278 // FIXME(eddyb) this is only left around for the validation logic
1279 // in `promote_consts`, see the comment in `validate_operand`.
1280 pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
1281
1282 fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
1283     // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1284     // cannot yet be stolen), because `mir_validated()`, which steals
1285     // from `mir_const(), forces this query to execute before
1286     // performing the steal.
1287     let body = &tcx.mir_const(def_id).borrow();
1288
1289     if body.return_ty().references_error() {
1290         tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
1291         return QUALIF_ERROR_BIT;
1292     }
1293
1294     Checker::new(tcx, def_id, body, Mode::Const).check_const()
1295 }
1296
1297 pub struct QualifyAndPromoteConstants<'tcx> {
1298     pub promoted: Cell<IndexVec<Promoted, Body<'tcx>>>,
1299 }
1300
1301 impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> {
1302     fn default() -> Self {
1303         QualifyAndPromoteConstants {
1304             promoted: Cell::new(IndexVec::new()),
1305         }
1306     }
1307 }
1308
1309 impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
1310     fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
1311         // There's not really any point in promoting errorful MIR.
1312         if body.return_ty().references_error() {
1313             tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
1314             return;
1315         }
1316
1317         if src.promoted.is_some() {
1318             return;
1319         }
1320
1321         let def_id = src.def_id();
1322         let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1323
1324         let mode = determine_mode(tcx, hir_id, def_id);
1325
1326         debug!("run_pass: mode={:?}", mode);
1327         if let Mode::NonConstFn = mode {
1328             // No need to const-check a non-const `fn` now that we don't do promotion here.
1329             return;
1330         } else if let Mode::ConstFn = mode {
1331             let mut checker = Checker::new(tcx, def_id, body, mode);
1332             let use_min_const_fn_checks =
1333                 !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
1334                 tcx.is_min_const_fn(def_id);
1335             if use_min_const_fn_checks {
1336                 // Enforce `min_const_fn` for stable `const fn`s.
1337                 use super::qualify_min_const_fn::is_min_const_fn;
1338                 if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1339                     error_min_const_fn_violation(tcx, span, err);
1340                     return;
1341                 }
1342             }
1343
1344             // `check_const` should not produce any errors, but better safe than sorry
1345             // FIXME(#53819)
1346             // Enforce a constant-like CFG for `const fn`.
1347             checker.check_const();
1348         } else {
1349             check_short_circuiting_in_const_local(tcx, body, mode);
1350
1351             match mode {
1352                 Mode::Const => tcx.mir_const_qualif(def_id),
1353                 _ => Checker::new(tcx, def_id, body, mode).check_const(),
1354             };
1355         }
1356
1357         if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
1358             // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
1359             check_static_is_sync(tcx, body, hir_id);
1360         }
1361     }
1362 }
1363
1364 fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
1365     match tcx.hir().body_owner_kind(hir_id) {
1366         hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1367         hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
1368         hir::BodyOwnerKind::Fn => Mode::NonConstFn,
1369         hir::BodyOwnerKind::Const => Mode::Const,
1370         hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1371         hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1372     }
1373 }
1374
1375 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
1376     struct_span_err!(tcx.sess, span, E0723, "{}", msg)
1377         .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
1378         .help("add `#![feature(const_fn)]` to the crate attributes to enable")
1379         .emit();
1380 }
1381
1382 fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
1383     if body.control_flow_destroyed.is_empty() {
1384         return;
1385     }
1386
1387     let mut locals = body.vars_iter();
1388     if let Some(local) = locals.next() {
1389         let span = body.local_decls[local].source_info.span;
1390         let mut error = tcx.sess.struct_span_err(
1391             span,
1392             &format!(
1393                 "new features like let bindings are not permitted in {}s \
1394                 which also use short circuiting operators",
1395                 mode,
1396             ),
1397         );
1398         for (span, kind) in body.control_flow_destroyed.iter() {
1399             error.span_note(
1400                 *span,
1401                 &format!("use of {} here does not actually short circuit due to \
1402                 the const evaluator presently not being able to do control flow. \
1403                 See https://github.com/rust-lang/rust/issues/49146 for more \
1404                 information.", kind),
1405             );
1406         }
1407         for local in locals {
1408             let span = body.local_decls[local].source_info.span;
1409             error.span_note(span, "more locals defined here");
1410         }
1411         error.emit();
1412     }
1413 }
1414
1415 fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
1416     let ty = body.return_ty();
1417     tcx.infer_ctxt().enter(|infcx| {
1418         let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1419         let mut fulfillment_cx = traits::FulfillmentContext::new();
1420         let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
1421         fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1422         if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1423             infcx.report_fulfillment_errors(&err, None, false);
1424         }
1425     });
1426 }
1427
1428 fn validator_mismatch(
1429     tcx: TyCtxt<'tcx>,
1430     body: &Body<'tcx>,
1431     mut old_errors: Vec<(Span, String)>,
1432     mut new_errors: Vec<(Span, String)>,
1433 ) {
1434     error!("old validator: {:?}", old_errors);
1435     error!("new validator: {:?}", new_errors);
1436
1437     // ICE on nightly if the validators do not emit exactly the same errors.
1438     // Users can supress this panic with an unstable compiler flag (hopefully after
1439     // filing an issue).
1440     let opts = &tcx.sess.opts;
1441     let strict_validation_enabled = opts.unstable_features.is_nightly_build()
1442         && !opts.debugging_opts.suppress_const_validation_back_compat_ice;
1443
1444     if !strict_validation_enabled {
1445         return;
1446     }
1447
1448     // If this difference would cause a regression from the old to the new or vice versa, trigger
1449     // the ICE.
1450     if old_errors.is_empty() || new_errors.is_empty() {
1451         span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1452     }
1453
1454     // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the
1455     // new validator to be more conservative about when a dropped local has been moved out of.
1456     //
1457     // Supress the mismatch ICE in cases where the validators disagree only on the number of
1458     // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s.
1459
1460     let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop");
1461     let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow");
1462
1463     let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect();
1464     let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect();
1465
1466     let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors;
1467
1468     let old_mut_borrows = old_errors.iter().filter(is_mut_borrow);
1469     let new_mut_borrows = new_errors.iter().filter(is_mut_borrow);
1470
1471     let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some();
1472
1473     if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) {
1474         return;
1475     }
1476
1477     span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1478 }
1479
1480 const VALIDATOR_MISMATCH_ERR: &str =
1481     r"Disagreement between legacy and dataflow-based const validators.
1482     After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code.";