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