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