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