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