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