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