1 //! A pass that qualifies constness of temporaries in constants,
2 //! static initializers and functions and also drives promotion.
4 //! The Qualif flags below can be used to also provide better
5 //! diagnostics as to why a constant rvalue wasn't promoted.
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;
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;
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};
31 use std::ops::{Deref, Index, IndexMut};
34 use rustc::hir::HirId;
35 use crate::transform::{MirPass, MirSource};
36 use super::promote_consts::{self, Candidate, TempState};
38 /// What kind of item we are in.
39 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
43 /// A `static mut` item.
45 /// A `const fn` item.
47 /// A `const` item or an anonymous constant (e.g. in array lengths).
49 /// Other type of `fn`.
54 /// Determine whether we have to do full const-checking because syntactically, we
55 /// are required to be "const".
57 fn requires_const_checking(self) -> bool {
58 self != Mode::NonConstFn
62 impl fmt::Display for Mode {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 Mode::Const => write!(f, "constant"),
66 Mode::Static | Mode::StaticMut => write!(f, "static"),
67 Mode::ConstFn => write!(f, "constant function"),
68 Mode::NonConstFn => write!(f, "function")
73 const QUALIF_COUNT: usize = 4;
75 // FIXME(eddyb) once we can use const generics, replace this array with
76 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
77 #[derive(Copy, Clone, Default)]
78 struct PerQualif<T>([T; QUALIF_COUNT]);
80 impl<T: Clone> PerQualif<T> {
81 fn new(x: T) -> Self {
82 PerQualif([x.clone(), x.clone(), x.clone(), x])
86 impl<T> PerQualif<T> {
87 fn as_mut(&mut self) -> PerQualif<&mut T> {
88 let [x0, x1, x2, x3] = &mut self.0;
89 PerQualif([x0, x1, x2, x3])
92 fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
93 let [x0, x1, x2, x3] = self.0;
94 let [y0, y1, y2, y3] = other.0;
95 PerQualif([(x0, y0), (x1, y1), (x2, y2), (x3, y3)])
99 impl PerQualif<bool> {
100 fn encode_to_bits(self) -> u8 {
101 self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
102 bits | ((qualif as u8) << i)
106 fn decode_from_bits(bits: u8) -> Self {
107 let mut qualifs = Self::default();
108 for (i, qualif) in qualifs.0.iter_mut().enumerate() {
109 *qualif = (bits & (1 << i)) != 0;
115 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
118 fn index(&self, _: Q) -> &T {
123 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
124 fn index_mut(&mut self, _: Q) -> &mut T {
129 struct ConstCx<'a, 'tcx> {
131 param_env: ty::ParamEnv<'tcx>,
133 body: &'a Body<'tcx>,
135 per_local: PerQualif<BitSet<Local>>,
138 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
139 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
140 Some(def_id) == self.tcx.lang_items().panic_fn() ||
141 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
145 #[derive(Copy, Clone, Debug)]
146 enum ValueSource<'a, 'tcx> {
147 Rvalue(&'a Rvalue<'tcx>),
148 DropAndReplace(&'a Operand<'tcx>),
150 callee: &'a Operand<'tcx>,
151 args: &'a [Operand<'tcx>],
156 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
157 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
158 /// "I found something bad, no reason to go on searching". `false` is only returned if we
159 /// definitely cannot find anything bad anywhere.
161 /// The default implementations proceed structurally.
165 /// Return the qualification that is (conservatively) correct for any value
166 /// of the type, or `None` if the qualification is not value/type-based.
167 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
171 /// Return a mask for the qualification, given a type. This is `false` iff
172 /// no value of that type can have the qualification.
173 fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
174 Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
177 fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
178 cx.per_local.0[Self::IDX].contains(local)
181 fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
182 // FIXME(eddyb) should we do anything here for value properties?
186 fn in_projection_structurally(
187 cx: &ConstCx<'_, 'tcx>,
188 place: PlaceRef<'_, 'tcx>,
190 if let [proj_base @ .., elem] = place.projection {
191 let base_qualif = Self::in_place(cx, PlaceRef {
193 projection: proj_base,
195 let qualif = base_qualif && Self::mask_for_ty(
197 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
198 .projection_ty(cx.tcx, elem)
202 ProjectionElem::Deref |
203 ProjectionElem::Subslice { .. } |
204 ProjectionElem::Field(..) |
205 ProjectionElem::ConstantIndex { .. } |
206 ProjectionElem::Downcast(..) => qualif,
208 ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
211 bug!("This should be called if projection is not empty");
216 cx: &ConstCx<'_, 'tcx>,
217 place: PlaceRef<'_, 'tcx>,
219 Self::in_projection_structurally(cx, place)
222 fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
225 base: PlaceBase::Local(local),
227 } => Self::in_local(cx, *local),
229 base: PlaceBase::Static(box Static {
230 kind: StaticKind::Promoted(..),
234 } => bug!("qualifying already promoted MIR"),
236 base: PlaceBase::Static(static_),
239 Self::in_static(cx, static_)
244 } => Self::in_projection(cx, place),
248 fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
250 Operand::Copy(ref place) |
251 Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
253 Operand::Constant(ref constant) => {
254 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
255 // Don't peek inside trait associated constants.
256 if cx.tcx.trait_of_item(def_id).is_some() {
257 Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
259 let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
261 let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
263 // Just in case the type is more specific than
264 // the definition, e.g., impl associated const
265 // with type parameters, take it into account.
266 qualif && Self::mask_for_ty(cx, constant.literal.ty)
275 fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
277 Rvalue::NullaryOp(..) => false,
279 Rvalue::Discriminant(ref place) |
280 Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
282 Rvalue::Use(ref operand) |
283 Rvalue::Repeat(ref operand, _) |
284 Rvalue::UnaryOp(_, ref operand) |
285 Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
287 Rvalue::BinaryOp(_, ref lhs, ref rhs) |
288 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
289 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
292 Rvalue::Ref(_, _, ref place) => {
293 // Special-case reborrows to be more like a copy of the reference.
294 if let box [proj_base @ .., elem] = &place.projection {
295 if ProjectionElem::Deref == *elem {
296 let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
297 if let ty::Ref(..) = base_ty.sty {
298 return Self::in_place(cx, PlaceRef {
300 projection: proj_base,
306 Self::in_place(cx, place.as_ref())
309 Rvalue::Aggregate(_, ref operands) => {
310 operands.iter().any(|o| Self::in_operand(cx, o))
315 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
316 Self::in_rvalue_structurally(cx, rvalue)
320 cx: &ConstCx<'_, 'tcx>,
321 _callee: &Operand<'tcx>,
322 _args: &[Operand<'tcx>],
325 // Be conservative about the returned value of a const fn.
326 Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
329 fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
331 ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
332 ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
333 ValueSource::Call { callee, args, return_ty } => {
334 Self::in_call(cx, callee, args, return_ty)
340 /// Constant containing interior mutability (`UnsafeCell<T>`).
341 /// This must be ruled out to make sure that evaluating the constant at compile-time
342 /// and at *any point* during the run-time would produce the same result. In particular,
343 /// promotion of temporaries must not change program behavior; if the promoted could be
344 /// written to, that would be a problem.
345 struct HasMutInterior;
347 impl Qualif for HasMutInterior {
348 const IDX: usize = 0;
350 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
351 Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
354 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
356 // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
357 // allowed in constants (and the `Checker` will error), and/or it
358 // won't be promoted, due to `&mut ...` or interior mutability.
359 Rvalue::Ref(_, kind, ref place) => {
360 let ty = place.ty(cx.body, cx.tcx).ty;
362 if let BorrowKind::Mut { .. } = kind {
363 // In theory, any zero-sized value could be borrowed
364 // mutably without consequences. However, only &mut []
365 // is allowed right now, and only in functions.
366 if cx.mode == Mode::StaticMut {
367 // Inside a `static mut`, &mut [...] is also allowed.
369 ty::Array(..) | ty::Slice(_) => {}
372 } else if let ty::Array(_, len) = ty.sty {
373 // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
374 // seems unnecessary, given that this is merely a ZST.
375 match len.try_eval_usize(cx.tcx, cx.param_env) {
376 Some(0) if cx.mode == Mode::NonConstFn => {},
385 Rvalue::Aggregate(ref kind, _) => {
386 if let AggregateKind::Adt(def, ..) = **kind {
387 if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
388 let ty = rvalue.ty(cx.body, cx.tcx);
389 assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
398 Self::in_rvalue_structurally(cx, rvalue)
402 /// Constant containing an ADT that implements `Drop`.
403 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
404 /// as that might not be a `const fn`, and (b) because implicit promotion would
405 /// remove side-effects that occur as part of dropping that value.
408 impl Qualif for NeedsDrop {
409 const IDX: usize = 1;
411 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
412 Some(ty.needs_drop(cx.tcx, cx.param_env))
415 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
416 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
417 if let AggregateKind::Adt(def, ..) = **kind {
418 if def.has_dtor(cx.tcx) {
424 Self::in_rvalue_structurally(cx, rvalue)
428 /// Not promotable at all - non-`const fn` calls, `asm!`,
429 /// pointer comparisons, ptr-to-int casts, etc.
430 /// Inside a const context all constness rules apply, so promotion simply has to follow the regular
431 /// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
432 /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
433 /// visitor enforces by emitting errors when working in const context.
434 struct IsNotPromotable;
436 impl Qualif for IsNotPromotable {
437 const IDX: usize = 2;
439 fn in_static(cx: &ConstCx<'_, 'tcx>, static_: &Static<'tcx>) -> bool {
441 StaticKind::Promoted(_, _) => unreachable!(),
442 StaticKind::Static => {
443 // Only allow statics (not consts) to refer to other statics.
444 let allowed = cx.mode == Mode::Static || cx.mode == Mode::StaticMut;
447 cx.tcx.get_attrs(static_.def_id).iter().any(
448 |attr| attr.check_name(sym::thread_local)
455 cx: &ConstCx<'_, 'tcx>,
456 place: PlaceRef<'_, 'tcx>,
458 if let [proj_base @ .., elem] = place.projection {
460 ProjectionElem::Deref |
461 ProjectionElem::Downcast(..) => return true,
463 ProjectionElem::ConstantIndex {..} |
464 ProjectionElem::Subslice {..} |
465 ProjectionElem::Index(_) => {}
467 ProjectionElem::Field(..) => {
468 if cx.mode == Mode::NonConstFn {
469 let base_ty = Place::ty_from(place.base, proj_base, cx.body, cx.tcx).ty;
470 if let Some(def) = base_ty.ty_adt_def() {
471 // No promotion of union field accesses.
480 Self::in_projection_structurally(cx, place)
482 bug!("This should be called if projection is not empty");
486 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
488 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
489 let operand_ty = operand.ty(cx.body, cx.tcx);
490 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
491 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
492 match (cast_in, cast_out) {
493 (CastTy::Ptr(_), CastTy::Int(_)) |
494 (CastTy::FnPtr, CastTy::Int(_)) => {
495 // in normal functions, mark such casts as not promotable
502 Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
503 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.body, cx.tcx).sty {
504 assert!(op == BinOp::Eq || op == BinOp::Ne ||
505 op == BinOp::Le || op == BinOp::Lt ||
506 op == BinOp::Ge || op == BinOp::Gt ||
507 op == BinOp::Offset);
509 // raw pointer operations are not allowed inside promoteds
514 Rvalue::NullaryOp(NullOp::Box, _) => return true,
519 Self::in_rvalue_structurally(cx, rvalue)
523 cx: &ConstCx<'_, 'tcx>,
524 callee: &Operand<'tcx>,
525 args: &[Operand<'tcx>],
526 _return_ty: Ty<'tcx>,
528 let fn_ty = callee.ty(cx.body, cx.tcx);
530 ty::FnDef(def_id, _) => {
531 match cx.tcx.fn_sig(def_id).abi() {
533 Abi::PlatformIntrinsic => {
534 assert!(!cx.tcx.is_const_fn(def_id));
535 match &cx.tcx.item_name(def_id).as_str()[..] {
554 | "add_with_overflow"
555 | "sub_with_overflow"
556 | "mul_with_overflow"
567 cx.tcx.is_const_fn(def_id) ||
568 cx.tcx.is_unstable_const_fn(def_id).is_some() ||
569 cx.is_const_panic_fn(def_id);
579 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
583 /// Refers to temporaries which cannot be promoted *implicitly*.
584 /// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
585 /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
586 /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
587 /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
588 struct IsNotImplicitlyPromotable;
590 impl Qualif for IsNotImplicitlyPromotable {
591 const IDX: usize = 3;
594 cx: &ConstCx<'_, 'tcx>,
595 callee: &Operand<'tcx>,
596 args: &[Operand<'tcx>],
597 _return_ty: Ty<'tcx>,
599 if cx.mode == Mode::NonConstFn {
600 if let ty::FnDef(def_id, _) = callee.ty(cx.body, cx.tcx).sty {
601 // Never promote runtime `const fn` calls of
602 // functions without `#[rustc_promotable]`.
603 if !cx.tcx.is_promotable_const_fn(def_id) {
609 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
613 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
614 macro_rules! static_assert_seq_qualifs {
615 ($i:expr => $first:ident $(, $rest:ident)*) => {
617 static_assert_seq_qualifs!($i + 1 => $($rest),*);
623 static_assert!(QUALIF_COUNT == $i);
626 static_assert_seq_qualifs!(
627 0 => HasMutInterior, NeedsDrop, IsNotPromotable, IsNotImplicitlyPromotable
630 impl ConstCx<'_, 'tcx> {
631 fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
632 let mut qualifs = PerQualif::default();
633 qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
634 qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
635 qualifs[IsNotPromotable] = IsNotPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
636 qualifs[IsNotImplicitlyPromotable] =
637 IsNotImplicitlyPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
641 fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
642 let mut qualifs = PerQualif::default();
643 qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
644 qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
645 qualifs[IsNotPromotable] = IsNotPromotable::in_local(self, local);
646 qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_local(self, local);
650 fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
651 let mut qualifs = PerQualif::default();
652 qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
653 qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
654 qualifs[IsNotPromotable] = IsNotPromotable::in_value(self, source);
655 qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_value(self, source);
660 /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
661 /// for value qualifications, and accumulates writes of
662 /// rvalue/call results to locals, in `local_qualif`.
663 /// It also records candidates for promotion in `promotion_candidates`,
664 /// both in functions and const/static items.
665 struct Checker<'a, 'tcx> {
666 cx: ConstCx<'a, 'tcx>,
670 rpo: ReversePostorder<'a, 'tcx>,
672 temp_promotion_state: IndexVec<Local, TempState>,
673 promotion_candidates: Vec<Candidate>,
676 macro_rules! unleash_miri {
678 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
679 $this.tcx.sess.span_warn($this.span, "skipping const checks");
685 impl Deref for Checker<'a, 'tcx> {
686 type Target = ConstCx<'a, 'tcx>;
688 fn deref(&self) -> &Self::Target {
693 impl<'a, 'tcx> Checker<'a, 'tcx> {
694 fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
695 assert!(def_id.is_local());
696 let mut rpo = traversal::reverse_postorder(body);
697 let temps = promote_consts::collect_temps(body, &mut rpo);
700 let param_env = tcx.param_env(def_id);
702 let mut cx = ConstCx {
707 per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
710 for (local, decl) in body.local_decls.iter_enumerated() {
711 if let LocalKind::Arg = body.local_kind(local) {
712 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
713 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
715 per_local.insert(local);
719 if !temps[local].is_promotable() {
720 cx.per_local[IsNotPromotable].insert(local);
722 if let LocalKind::Var = body.local_kind(local) {
723 // Sanity check to prevent implicit and explicit promotion of
725 assert!(cx.per_local[IsNotPromotable].contains(local));
734 temp_promotion_state: temps,
735 promotion_candidates: vec![]
739 // FIXME(eddyb) we could split the errors into meaningful
740 // categories, but enabling full miri would make that
741 // slightly pointless (even with feature-gating).
742 fn not_const(&mut self) {
744 if self.mode.requires_const_checking() {
745 let mut err = struct_span_err!(
749 "{} contains unimplemented expression type",
752 if self.tcx.sess.teach(&err.get_code().unwrap()) {
753 err.note("A function call isn't allowed in the const's initialization expression \
754 because the expression's value must be known at compile-time.");
755 err.note("Remember: you can't use a function call inside a const's initialization \
756 expression! However, you can use it anywhere else.");
762 /// Assigns an rvalue/call qualification to the given destination.
763 fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
764 trace!("assign: {:?} <- {:?}", dest, source);
766 let mut qualifs = self.qualifs_in_value(source);
769 ValueSource::Rvalue(&Rvalue::Ref(_, kind, ref place)) => {
770 // Getting `true` from `HasMutInterior::in_rvalue` means
771 // the borrowed place is disallowed from being borrowed,
772 // due to either a mutable borrow (with some exceptions),
773 // or an shared borrow of a value with interior mutability.
774 // Then `HasMutInterior` is replaced with `IsNotPromotable`,
775 // to avoid duplicate errors (e.g. from reborrowing).
776 if qualifs[HasMutInterior] {
777 qualifs[HasMutInterior] = false;
778 qualifs[IsNotPromotable] = true;
780 if self.mode.requires_const_checking() {
781 if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
782 if let BorrowKind::Mut { .. } = kind {
783 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
784 "references in {}s may only refer \
785 to immutable values", self.mode);
786 err.span_label(self.span, format!("{}s require immutable values",
788 if self.tcx.sess.teach(&err.get_code().unwrap()) {
789 err.note("References in statics and constants may only refer \
790 to immutable values.\n\n\
791 Statics are shared everywhere, and if they refer to \
792 mutable data one might violate memory safety since \
793 holding multiple mutable references to shared data \
795 If you really want global mutable state, try using \
796 static mut or a global UnsafeCell.");
800 span_err!(self.tcx.sess, self.span, E0492,
801 "cannot borrow a constant which may contain \
802 interior mutability, create a static instead");
806 } else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
807 // Don't promote BorrowKind::Shallow borrows, as they don't
810 // We might have a candidate for promotion.
811 let candidate = Candidate::Ref(location);
812 // Start by traversing to the "base", with non-deref projections removed.
814 place.projection.iter().rev().find(|&elem| *elem == ProjectionElem::Deref);
817 "qualify_consts: promotion candidate: place={:?} {:?}",
818 place.base, deref_proj
820 // We can only promote interior borrows of promotable temps (non-temps
821 // don't get promoted anyway).
822 // (If we bailed out of the loop due to a `Deref` above, we will definitely
823 // not enter the conditional here.)
824 if let (PlaceBase::Local(local), None) = (&place.base, deref_proj) {
825 if self.body.local_kind(*local) == LocalKind::Temp {
826 debug!("qualify_consts: promotion candidate: local={:?}", local);
827 // The borrowed place doesn't have `HasMutInterior`
828 // (from `in_rvalue`), so we can safely ignore
829 // `HasMutInterior` from the local's qualifications.
830 // This allows borrowing fields which don't have
831 // `HasMutInterior`, from a type that does, e.g.:
832 // `let _: &'static _ = &(Cell::new(1), 2).1;`
833 let mut local_qualifs = self.qualifs_in_local(*local);
834 // Any qualifications, except HasMutInterior (see above), disqualify
836 // This is, in particular, the "implicit promotion" version of
837 // the check making sure that we don't run drop glue during const-eval.
838 local_qualifs[HasMutInterior] = false;
839 if !local_qualifs.0.iter().any(|&qualif| qualif) {
840 debug!("qualify_consts: promotion candidate: {:?}", candidate);
841 self.promotion_candidates.push(candidate);
847 ValueSource::Rvalue(&Rvalue::Repeat(ref operand, _)) => {
848 let candidate = Candidate::Repeat(location);
849 let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) ||
850 IsNotPromotable::in_operand(self, operand);
851 debug!("assign: self.def_id={:?} operand={:?}", self.def_id, operand);
852 if !not_promotable && self.tcx.features().const_in_array_repeat_expressions {
853 debug!("assign: candidate={:?}", candidate);
854 self.promotion_candidates.push(candidate);
860 let mut dest_projection = &dest.projection[..];
862 match (&dest.base, dest_projection) {
863 // We treat all locals equal in constants
864 (&PlaceBase::Local(index), []) => break index,
865 // projections are transparent for assignments
866 // we qualify the entire destination at once, even if just a field would have
867 // stricter qualification
868 (base, [proj_base @ .., _]) => {
869 // Catch more errors in the destination. `visit_place` also checks various
870 // projection rules like union field access and raw pointer deref
871 let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
872 self.visit_place_base(base, context, location);
873 self.visit_projection(base, dest_projection, context, location);
874 dest_projection = proj_base;
876 (&PlaceBase::Static(box Static {
877 kind: StaticKind::Promoted(..),
879 }), []) => bug!("promoteds don't exist yet during promotion"),
880 (&PlaceBase::Static(box Static{ kind: _, .. }), []) => {
881 // Catch more errors in the destination. `visit_place` also checks that we
882 // do not try to access statics from constants or try to mutate statics
883 let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
884 self.visit_place_base(&dest.base, context, location);
890 let kind = self.body.local_kind(index);
891 debug!("store to {:?} {:?}", kind, index);
893 // Only handle promotable temps in non-const functions.
894 if self.mode == Mode::NonConstFn {
895 if kind != LocalKind::Temp ||
896 !self.temp_promotion_state[index].is_promotable() {
901 // this is overly restrictive, because even full assignments do not clear the qualif
902 // While we could special case full assignments, this would be inconsistent with
903 // aggregates where we overwrite all fields via assignments, which would not get
905 for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
907 per_local.insert(index);
911 // Ensure the `IsNotPromotable` qualification is preserved.
912 // NOTE(eddyb) this is actually unnecessary right now, as
913 // we never replace the local's qualif, but we might in
914 // the future, and so it serves to catch changes that unset
915 // important bits (in which case, asserting `contains` could
916 // be replaced with calling `insert` to re-set the bit).
917 if kind == LocalKind::Temp {
918 if !self.temp_promotion_state[index].is_promotable() {
919 assert!(self.cx.per_local[IsNotPromotable].contains(index));
924 /// Check a whole const, static initializer or const fn.
925 fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
926 debug!("const-checking {} {:?}", self.mode, self.def_id);
928 let body = self.body;
930 let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
931 let mut bb = START_BLOCK;
933 seen_blocks.insert(bb.index());
935 self.visit_basic_block_data(bb, &body[bb]);
937 let target = match body[bb].terminator().kind {
938 TerminatorKind::Goto { target } |
939 TerminatorKind::FalseUnwind { real_target: target, .. } |
940 TerminatorKind::Drop { target, .. } |
941 TerminatorKind::DropAndReplace { target, .. } |
942 TerminatorKind::Assert { target, .. } |
943 TerminatorKind::Call { destination: Some((_, target)), .. } => {
947 // Non-terminating calls cannot produce any value.
948 TerminatorKind::Call { destination: None, .. } => {
952 TerminatorKind::SwitchInt {..} |
953 TerminatorKind::Resume |
954 TerminatorKind::Abort |
955 TerminatorKind::GeneratorDrop |
956 TerminatorKind::Yield { .. } |
957 TerminatorKind::Unreachable |
958 TerminatorKind::FalseEdges { .. } => None,
960 TerminatorKind::Return => {
967 Some(target) if !seen_blocks.contains(target.index()) => {
978 // Collect all the temps we need to promote.
979 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
981 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
982 for candidate in &self.promotion_candidates {
984 Candidate::Repeat(Location { block: bb, statement_index: stmt_idx }) => {
985 if let StatementKind::Assign(_, box Rvalue::Repeat(
986 Operand::Move(Place {
987 base: PlaceBase::Local(index),
991 )) = self.body[bb].statements[stmt_idx].kind {
992 promoted_temps.insert(index);
995 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
996 if let StatementKind::Assign(
998 box Rvalue::Ref(_, _, Place {
999 base: PlaceBase::Local(index),
1002 ) = self.body[bb].statements[stmt_idx].kind {
1003 promoted_temps.insert(index);
1006 Candidate::Argument { .. } => {}
1010 let mut qualifs = self.qualifs_in_local(RETURN_PLACE);
1012 // Account for errors in consts by using the
1013 // conservative type qualification instead.
1014 if qualifs[IsNotPromotable] {
1015 qualifs = self.qualifs_in_any_value_of_ty(body.return_ty());
1018 (qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
1022 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1023 fn visit_place_base(
1025 place_base: &PlaceBase<'tcx>,
1026 context: PlaceContext,
1029 self.super_place_base(place_base, context, location);
1031 PlaceBase::Local(_) => {}
1032 PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
1035 PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
1039 .any(|attr| attr.check_name(sym::thread_local)) {
1040 if self.mode.requires_const_checking() {
1041 span_err!(self.tcx.sess, self.span, E0625,
1042 "thread-local statics cannot be \
1043 accessed at compile-time");
1048 // Only allow statics (not consts) to refer to other statics.
1049 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
1050 if self.mode == Mode::Static && context.is_mutating_use() {
1051 // this is not strictly necessary as miri will also bail out
1052 // For interior mutability we can't really catch this statically as that
1053 // goes through raw pointers and intermediate temporaries, so miri has
1054 // to catch this anyway
1055 self.tcx.sess.span_err(
1057 "cannot mutate statics in the initializer of another static",
1062 unleash_miri!(self);
1064 if self.mode.requires_const_checking() {
1065 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
1066 "{}s cannot refer to statics, use \
1067 a constant instead", self.mode);
1068 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1070 "Static and const variables can refer to other const variables. \
1071 But a const variable cannot refer to a static variable."
1074 "To fix this, the value can be extracted as a const and then used."
1083 fn visit_projection(
1085 place_base: &PlaceBase<'tcx>,
1086 proj: &[PlaceElem<'tcx>],
1087 context: PlaceContext,
1091 "visit_place_projection: proj={:?} context={:?} location={:?}",
1092 proj, context, location,
1094 self.super_projection(place_base, proj, context, location);
1096 if let [proj_base @ .., elem] = proj {
1098 ProjectionElem::Deref => {
1099 if context.is_mutating_use() {
1100 // `not_const` errors out in const contexts
1103 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
1105 Mode::NonConstFn => {},
1107 if let ty::RawPtr(_) = base_ty.sty {
1108 if !self.tcx.features().const_raw_ptr_deref {
1110 &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
1111 self.span, GateIssue::Language,
1113 "dereferencing raw pointers in {}s is unstable",
1123 ProjectionElem::ConstantIndex {..} |
1124 ProjectionElem::Subslice {..} |
1125 ProjectionElem::Field(..) |
1126 ProjectionElem::Index(_) => {
1127 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
1128 if let Some(def) = base_ty.ty_adt_def() {
1132 if !self.tcx.features().const_fn_union {
1134 &self.tcx.sess.parse_sess, sym::const_fn_union,
1135 self.span, GateIssue::Language,
1136 "unions in const fn are unstable",
1151 ProjectionElem::Downcast(..) => {
1158 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
1159 debug!("visit_operand: operand={:?} location={:?}", operand, location);
1160 self.super_operand(operand, location);
1163 Operand::Move(ref place) => {
1164 // Mark the consumed locals to indicate later drops are noops.
1166 base: PlaceBase::Local(local),
1169 self.cx.per_local[NeedsDrop].remove(local);
1173 Operand::Constant(_) => {}
1177 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1178 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
1180 // Check nested operands and places.
1181 if let Rvalue::Ref(_, kind, ref place) = *rvalue {
1182 // Special-case reborrows.
1183 let mut reborrow_place = None;
1184 if let box [proj_base @ .., elem] = &place.projection {
1185 if *elem == ProjectionElem::Deref {
1186 let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
1187 if let ty::Ref(..) = base_ty.sty {
1188 reborrow_place = Some(proj_base);
1193 if let Some(proj) = reborrow_place {
1194 let ctx = match kind {
1195 BorrowKind::Shared => PlaceContext::NonMutatingUse(
1196 NonMutatingUseContext::SharedBorrow,
1198 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
1199 NonMutatingUseContext::ShallowBorrow,
1201 BorrowKind::Unique => PlaceContext::NonMutatingUse(
1202 NonMutatingUseContext::UniqueBorrow,
1204 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
1205 MutatingUseContext::Borrow,
1208 self.visit_place_base(&place.base, ctx, location);
1209 self.visit_projection(&place.base, proj, ctx, location);
1211 self.super_rvalue(rvalue, location);
1214 self.super_rvalue(rvalue, location);
1219 Rvalue::Repeat(..) |
1220 Rvalue::UnaryOp(UnOp::Neg, _) |
1221 Rvalue::UnaryOp(UnOp::Not, _) |
1222 Rvalue::NullaryOp(NullOp::SizeOf, _) |
1223 Rvalue::CheckedBinaryOp(..) |
1224 Rvalue::Cast(CastKind::Pointer(_), ..) |
1225 Rvalue::Discriminant(..) |
1228 Rvalue::Aggregate(..) => {}
1230 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
1231 let operand_ty = operand.ty(self.body, self.tcx);
1232 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
1233 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
1234 match (cast_in, cast_out) {
1235 (CastTy::Ptr(_), CastTy::Int(_)) |
1236 (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
1237 unleash_miri!(self);
1238 if !self.tcx.features().const_raw_ptr_to_usize_cast {
1239 // in const fn and constants require the feature gate
1240 // FIXME: make it unsafe inside const fn and constants
1242 &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
1243 self.span, GateIssue::Language,
1245 "casting pointers to integers in {}s is unstable",
1255 Rvalue::BinaryOp(op, ref lhs, _) => {
1256 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).sty {
1257 assert!(op == BinOp::Eq || op == BinOp::Ne ||
1258 op == BinOp::Le || op == BinOp::Lt ||
1259 op == BinOp::Ge || op == BinOp::Gt ||
1260 op == BinOp::Offset);
1262 unleash_miri!(self);
1263 if self.mode.requires_const_checking() &&
1264 !self.tcx.features().const_compare_raw_pointers
1266 // require the feature gate inside constants and const fn
1267 // FIXME: make it unsafe to use these operations
1269 &self.tcx.sess.parse_sess,
1270 sym::const_compare_raw_pointers,
1272 GateIssue::Language,
1273 &format!("comparing raw pointers inside {}", self.mode),
1279 Rvalue::NullaryOp(NullOp::Box, _) => {
1280 unleash_miri!(self);
1281 if self.mode.requires_const_checking() {
1282 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1283 "allocations are not allowed in {}s", self.mode);
1284 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1285 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1287 "The value of statics and constants must be known at compile time, \
1288 and they live for the entire lifetime of a program. Creating a boxed \
1289 value allocates memory on the heap at runtime, and therefore cannot \
1290 be done at compile time."
1299 fn visit_terminator_kind(&mut self,
1300 kind: &TerminatorKind<'tcx>,
1301 location: Location) {
1302 debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
1303 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1304 if let Some((ref dest, _)) = *destination {
1305 self.assign(dest, ValueSource::Call {
1308 return_ty: dest.ty(self.body, self.tcx).ty,
1312 let fn_ty = func.ty(self.body, self.tcx);
1313 let mut callee_def_id = None;
1314 let mut is_shuffle = false;
1316 ty::FnDef(def_id, _) => {
1317 callee_def_id = Some(def_id);
1318 match self.tcx.fn_sig(def_id).abi() {
1319 Abi::RustIntrinsic |
1320 Abi::PlatformIntrinsic => {
1321 assert!(!self.tcx.is_const_fn(def_id));
1322 match &self.tcx.item_name(def_id).as_str()[..] {
1323 // special intrinsic that can be called diretly without an intrinsic
1324 // feature gate needs a language feature gate
1326 if self.mode.requires_const_checking() {
1327 // const eval transmute calls only with the feature gate
1328 if !self.tcx.features().const_transmute {
1330 &self.tcx.sess.parse_sess, sym::const_transmute,
1331 self.span, GateIssue::Language,
1332 &format!("The use of std::mem::transmute() \
1333 is gated in {}s", self.mode));
1338 name if name.starts_with("simd_shuffle") => {
1342 // no need to check feature gates, intrinsics are only callable
1343 // from the libstd or with forever unstable feature gates
1348 // In normal functions no calls are feature-gated.
1349 if self.mode.requires_const_checking() {
1350 let unleash_miri = self
1355 .unleash_the_miri_inside_of_you;
1356 if self.tcx.is_const_fn(def_id) || unleash_miri {
1357 // stable const fns or unstable const fns
1358 // with their feature gate active
1359 // FIXME(eddyb) move stability checks from `is_const_fn` here.
1360 } else if self.is_const_panic_fn(def_id) {
1361 // Check the const_panic feature gate.
1362 // FIXME: cannot allow this inside `allow_internal_unstable`
1363 // because that would make `panic!` insta stable in constants,
1364 // since the macro is marked with the attribute.
1365 if !self.tcx.features().const_panic {
1366 // Don't allow panics in constants without the feature gate.
1368 &self.tcx.sess.parse_sess,
1371 GateIssue::Language,
1372 &format!("panicking in {}s is unstable", self.mode),
1375 } else if let Some(feature)
1376 = self.tcx.is_unstable_const_fn(def_id) {
1377 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1378 // functions without the feature gate active in this crate in
1379 // order to report a better error message than the one below.
1380 if !self.span.allows_unstable(feature) {
1381 let mut err = self.tcx.sess.struct_span_err(self.span,
1382 &format!("`{}` is not yet stable as a const fn",
1383 self.tcx.def_path_str(def_id)));
1384 if nightly_options::is_nightly_build() {
1386 "add `#![feature({})]` to the \
1387 crate attributes to enable",
1393 let mut err = struct_span_err!(
1397 "calls in {}s are limited to constant functions, \
1398 tuple structs and tuple variants",
1408 if self.mode.requires_const_checking() {
1409 let mut err = self.tcx.sess.struct_span_err(
1411 &format!("function pointers are not allowed in const fn"));
1420 // No need to do anything in constants and statics, as everything is "constant" anyway
1421 // so promotion would be useless.
1422 if self.mode != Mode::Static && self.mode != Mode::Const {
1423 let constant_args = callee_def_id.and_then(|id| {
1424 args_required_const(self.tcx, id)
1425 }).unwrap_or_default();
1426 for (i, arg) in args.iter().enumerate() {
1427 if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
1431 let candidate = Candidate::Argument { bb: location.block, index: i };
1432 // Since the argument is required to be constant,
1433 // we care about constness, not promotability.
1434 // If we checked for promotability, we'd miss out on
1435 // the results of function calls (which are never promoted
1436 // in runtime code).
1437 // This is not a problem, because the argument explicitly
1438 // requests constness, in contrast to regular promotion
1439 // which happens even without the user requesting it.
1440 // We can error out with a hard error if the argument is not
1442 if !IsNotPromotable::in_operand(self, arg) {
1443 debug!("visit_terminator_kind: candidate={:?}", candidate);
1444 self.promotion_candidates.push(candidate);
1447 span_err!(self.tcx.sess, self.span, E0526,
1448 "shuffle indices are not constant");
1450 self.tcx.sess.span_err(self.span,
1451 &format!("argument {} is required to be a constant",
1458 // Check callee and argument operands.
1459 self.visit_operand(func, location);
1461 self.visit_operand(arg, location);
1463 } else if let TerminatorKind::Drop {
1464 location: ref place, ..
1465 } | TerminatorKind::DropAndReplace {
1466 location: ref place, ..
1469 TerminatorKind::DropAndReplace { .. } => {}
1470 _ => self.super_terminator_kind(kind, location),
1473 // Deny *any* live drops anywhere other than functions.
1474 if self.mode.requires_const_checking() {
1475 unleash_miri!(self);
1476 // HACK(eddyb): emulate a bit of dataflow analysis,
1477 // conservatively, that drop elaboration will do.
1478 let needs_drop = if let Place {
1479 base: PlaceBase::Local(local),
1482 if NeedsDrop::in_local(self, local) {
1483 Some(self.body.local_decls[local].source_info.span)
1491 if let Some(span) = needs_drop {
1492 // Double-check the type being dropped, to minimize false positives.
1493 let ty = place.ty(self.body, self.tcx).ty;
1494 if ty.needs_drop(self.tcx, self.param_env) {
1495 struct_span_err!(self.tcx.sess, span, E0493,
1496 "destructors cannot be evaluated at compile-time")
1497 .span_label(span, format!("{}s cannot evaluate destructors",
1505 TerminatorKind::DropAndReplace { ref value, .. } => {
1506 self.assign(place, ValueSource::DropAndReplace(value), location);
1507 self.visit_operand(value, location);
1512 // Qualify any operands inside other terminators.
1513 self.super_terminator_kind(kind, location);
1517 fn visit_assign(&mut self,
1519 rvalue: &Rvalue<'tcx>,
1520 location: Location) {
1521 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1522 self.assign(dest, ValueSource::Rvalue(rvalue), location);
1524 self.visit_rvalue(rvalue, location);
1527 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1528 debug!("visit_source_info: source_info={:?}", source_info);
1529 self.span = source_info.span;
1532 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1533 debug!("visit_statement: statement={:?} location={:?}", statement, location);
1534 match statement.kind {
1535 StatementKind::Assign(..) => {
1536 self.super_statement(statement, location);
1538 StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
1541 // FIXME(eddyb) should these really do nothing?
1542 StatementKind::FakeRead(..) |
1543 StatementKind::SetDiscriminant { .. } |
1544 StatementKind::StorageLive(_) |
1545 StatementKind::StorageDead(_) |
1546 StatementKind::InlineAsm {..} |
1547 StatementKind::Retag { .. } |
1548 StatementKind::AscribeUserType(..) |
1549 StatementKind::Nop => {}
1554 pub fn provide(providers: &mut Providers<'_>) {
1555 *providers = Providers {
1561 fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
1562 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1563 // cannot yet be stolen), because `mir_validated()`, which steals
1564 // from `mir_const(), forces this query to execute before
1565 // performing the steal.
1566 let body = &tcx.mir_const(def_id).borrow();
1568 if body.return_ty().references_error() {
1569 tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
1570 return (1 << IsNotPromotable::IDX, tcx.arena.alloc(BitSet::new_empty(0)));
1573 Checker::new(tcx, def_id, body, Mode::Const).check_const()
1576 pub struct QualifyAndPromoteConstants<'tcx> {
1577 pub promoted: Cell<IndexVec<Promoted, Body<'tcx>>>,
1580 impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> {
1581 fn default() -> Self {
1582 QualifyAndPromoteConstants {
1583 promoted: Cell::new(IndexVec::new()),
1588 impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
1589 fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
1590 // There's not really any point in promoting errorful MIR.
1591 if body.return_ty().references_error() {
1592 tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
1596 if src.promoted.is_some() {
1600 let def_id = src.def_id();
1601 let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1603 let mode = determine_mode(tcx, hir_id, def_id);
1605 debug!("run_pass: mode={:?}", mode);
1606 if let Mode::NonConstFn | Mode::ConstFn = mode {
1607 // This is ugly because Checker holds onto mir,
1608 // which can't be mutated until its scope ends.
1609 let (temps, candidates) = {
1610 let mut checker = Checker::new(tcx, def_id, body, mode);
1611 if let Mode::ConstFn = mode {
1612 if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
1613 checker.check_const();
1614 } else if tcx.is_min_const_fn(def_id) {
1615 // Enforce `min_const_fn` for stable `const fn`s.
1616 use super::qualify_min_const_fn::is_min_const_fn;
1617 if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1618 error_min_const_fn_violation(tcx, span, err);
1620 // this should not produce any errors, but better safe than sorry
1622 checker.check_const();
1625 // Enforce a constant-like CFG for `const fn`.
1626 checker.check_const();
1629 while let Some((bb, data)) = checker.rpo.next() {
1630 checker.visit_basic_block_data(bb, data);
1634 (checker.temp_promotion_state, checker.promotion_candidates)
1637 // Do the actual promotion, now that we know what's viable.
1639 promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
1642 check_short_circuiting_in_const_local(tcx, body, mode);
1644 let promoted_temps = match mode {
1645 Mode::Const => tcx.mir_const_qualif(def_id).1,
1646 _ => Checker::new(tcx, def_id, body, mode).check_const().1,
1648 remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
1651 if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
1652 // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
1653 check_static_is_sync(tcx, body, hir_id);
1658 fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
1659 match tcx.hir().body_owner_kind(hir_id) {
1660 hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1661 hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
1662 hir::BodyOwnerKind::Fn => Mode::NonConstFn,
1663 hir::BodyOwnerKind::Const => Mode::Const,
1664 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1665 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1669 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
1670 struct_span_err!(tcx.sess, span, E0723, "{}", msg)
1671 .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
1672 .help("add `#![feature(const_fn)]` to the crate attributes to enable")
1676 fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
1677 if body.control_flow_destroyed.is_empty() {
1681 let mut locals = body.vars_iter();
1682 if let Some(local) = locals.next() {
1683 let span = body.local_decls[local].source_info.span;
1684 let mut error = tcx.sess.struct_span_err(
1687 "new features like let bindings are not permitted in {}s \
1688 which also use short circuiting operators",
1692 for (span, kind) in body.control_flow_destroyed.iter() {
1695 &format!("use of {} here does not actually short circuit due to \
1696 the const evaluator presently not being able to do control flow. \
1697 See https://github.com/rust-lang/rust/issues/49146 for more \
1698 information.", kind),
1701 for local in locals {
1702 let span = body.local_decls[local].source_info.span;
1703 error.span_note(span, "more locals defined here");
1709 /// In `const` and `static` everything without `StorageDead`
1710 /// is `'static`, we don't have to create promoted MIR fragments,
1711 /// just remove `Drop` and `StorageDead` on "promoted" locals.
1712 fn remove_drop_and_storage_dead_on_promoted_locals(
1713 body: &mut Body<'tcx>,
1714 promoted_temps: &BitSet<Local>,
1716 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1718 for block in body.basic_blocks_mut() {
1719 block.statements.retain(|statement| {
1720 match statement.kind {
1721 StatementKind::StorageDead(index) => !promoted_temps.contains(index),
1725 let terminator = block.terminator_mut();
1726 match terminator.kind {
1727 TerminatorKind::Drop {
1729 base: PlaceBase::Local(index),
1734 } if promoted_temps.contains(index) => {
1735 terminator.kind = TerminatorKind::Goto { target };
1742 fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
1743 let ty = body.return_ty();
1744 tcx.infer_ctxt().enter(|infcx| {
1745 let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1746 let mut fulfillment_cx = traits::FulfillmentContext::new();
1747 let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
1748 fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1749 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1750 infcx.report_fulfillment_errors(&err, None, false);
1755 fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1756 let attrs = tcx.get_attrs(def_id);
1757 let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
1758 let mut ret = FxHashSet::default();
1759 for meta in attr.meta_item_list()? {
1760 match meta.literal()?.node {
1761 LitKind::Int(a, _) => { ret.insert(a as usize); }