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