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};
29 use std::ops::{Deref, Index, IndexMut};
32 use crate::transform::{MirPass, MirSource};
33 use super::promote_consts::{self, Candidate, TempState};
35 /// What kind of item we are in.
36 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
40 /// A `static mut` item.
42 /// A `const fn` item.
44 /// A `const` item or an anonymous constant (e.g. in array lengths).
46 /// Other type of `fn`.
51 /// Determine whether we have to do full const-checking because syntactically, we
52 /// are required to be "const".
54 fn requires_const_checking(self) -> bool {
55 self != Mode::NonConstFn
59 impl fmt::Display for Mode {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 Mode::Const => write!(f, "constant"),
63 Mode::Static | Mode::StaticMut => write!(f, "static"),
64 Mode::ConstFn => write!(f, "constant function"),
65 Mode::NonConstFn => write!(f, "function")
70 const QUALIF_COUNT: usize = 4;
72 // FIXME(eddyb) once we can use const generics, replace this array with
73 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
74 #[derive(Copy, Clone, Default)]
75 struct PerQualif<T>([T; QUALIF_COUNT]);
77 impl<T: Clone> PerQualif<T> {
78 fn new(x: T) -> Self {
79 PerQualif([x.clone(), x.clone(), x.clone(), x])
83 impl<T> PerQualif<T> {
84 fn as_mut(&mut self) -> PerQualif<&mut T> {
85 let [x0, x1, x2, x3] = &mut self.0;
86 PerQualif([x0, x1, x2, x3])
89 fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
90 let [x0, x1, x2, x3] = self.0;
91 let [y0, y1, y2, y3] = other.0;
92 PerQualif([(x0, y0), (x1, y1), (x2, y2), (x3, y3)])
96 impl PerQualif<bool> {
97 fn encode_to_bits(self) -> u8 {
98 self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
99 bits | ((qualif as u8) << i)
103 fn decode_from_bits(bits: u8) -> Self {
104 let mut qualifs = Self::default();
105 for (i, qualif) in qualifs.0.iter_mut().enumerate() {
106 *qualif = (bits & (1 << i)) != 0;
112 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
115 fn index(&self, _: Q) -> &T {
120 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
121 fn index_mut(&mut self, _: Q) -> &mut T {
126 struct ConstCx<'a, 'tcx> {
127 tcx: TyCtxt<'tcx, 'tcx>,
128 param_env: ty::ParamEnv<'tcx>,
130 body: &'a Body<'tcx>,
132 per_local: PerQualif<BitSet<Local>>,
135 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
136 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
137 Some(def_id) == self.tcx.lang_items().panic_fn() ||
138 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
142 #[derive(Copy, Clone, Debug)]
143 enum ValueSource<'a, 'tcx> {
144 Rvalue(&'a Rvalue<'tcx>),
146 callee: &'a Operand<'tcx>,
147 args: &'a [Operand<'tcx>],
152 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
153 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
154 /// "I found something bad, no reason to go on searching". `false` is only returned if we
155 /// definitely cannot find anything bad anywhere.
157 /// The default implementations proceed structurally.
161 /// Return the qualification that is (conservatively) correct for any value
162 /// of the type, or `None` if the qualification is not value/type-based.
163 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
167 /// Return a mask for the qualification, given a type. This is `false` iff
168 /// no value of that type can have the qualification.
169 fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
170 Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
173 fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
174 cx.per_local.0[Self::IDX].contains(local)
177 fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
178 // FIXME(eddyb) should we do anything here for value properties?
182 fn in_projection_structurally(
183 cx: &ConstCx<'_, 'tcx>,
184 proj: &Projection<'tcx>,
186 let base_qualif = Self::in_place(cx, &proj.base);
187 let qualif = base_qualif && Self::mask_for_ty(
189 proj.base.ty(cx.body, cx.tcx)
190 .projection_ty(cx.tcx, &proj.elem)
194 ProjectionElem::Deref |
195 ProjectionElem::Subslice { .. } |
196 ProjectionElem::Field(..) |
197 ProjectionElem::ConstantIndex { .. } |
198 ProjectionElem::Downcast(..) => qualif,
200 ProjectionElem::Index(local) => qualif || Self::in_local(cx, local),
204 fn in_projection(cx: &ConstCx<'_, 'tcx>, proj: &Projection<'tcx>) -> bool {
205 Self::in_projection_structurally(cx, proj)
208 fn in_place(cx: &ConstCx<'_, 'tcx>, place: &Place<'tcx>) -> bool {
210 Place::Base(PlaceBase::Local(local)) => Self::in_local(cx, local),
211 Place::Base(PlaceBase::Static(box Static {kind: StaticKind::Promoted(_), .. })) =>
212 bug!("qualifying already promoted MIR"),
213 Place::Base(PlaceBase::Static(ref static_)) => {
214 Self::in_static(cx, static_)
216 Place::Projection(ref proj) => Self::in_projection(cx, proj),
220 fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
222 Operand::Copy(ref place) |
223 Operand::Move(ref place) => Self::in_place(cx, place),
225 Operand::Constant(ref constant) => {
226 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
227 // Don't peek inside trait associated constants.
228 if cx.tcx.trait_of_item(def_id).is_some() {
229 Self::in_any_value_of_ty(cx, constant.ty).unwrap_or(false)
231 let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
233 let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
235 // Just in case the type is more specific than
236 // the definition, e.g., impl associated const
237 // with type parameters, take it into account.
238 qualif && Self::mask_for_ty(cx, constant.ty)
247 fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
249 Rvalue::NullaryOp(..) => false,
251 Rvalue::Discriminant(ref place) |
252 Rvalue::Len(ref place) => Self::in_place(cx, place),
254 Rvalue::Use(ref operand) |
255 Rvalue::Repeat(ref operand, _) |
256 Rvalue::UnaryOp(_, ref operand) |
257 Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
259 Rvalue::BinaryOp(_, ref lhs, ref rhs) |
260 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
261 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
264 Rvalue::Ref(_, _, ref place) => {
265 // Special-case reborrows to be more like a copy of the reference.
266 if let Place::Projection(ref proj) = *place {
267 if let ProjectionElem::Deref = proj.elem {
268 let base_ty = proj.base.ty(cx.body, cx.tcx).ty;
269 if let ty::Ref(..) = base_ty.sty {
270 return Self::in_place(cx, &proj.base);
275 Self::in_place(cx, place)
278 Rvalue::Aggregate(_, ref operands) => {
279 operands.iter().any(|o| Self::in_operand(cx, o))
284 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
285 Self::in_rvalue_structurally(cx, rvalue)
289 cx: &ConstCx<'_, 'tcx>,
290 _callee: &Operand<'tcx>,
291 _args: &[Operand<'tcx>],
294 // Be conservative about the returned value of a const fn.
295 Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
298 fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
300 ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
301 ValueSource::Call { callee, args, return_ty } => {
302 Self::in_call(cx, callee, args, return_ty)
308 /// Constant containing interior mutability (`UnsafeCell<T>`).
309 /// This must be ruled out to make sure that evaluating the constant at compile-time
310 /// and at *any point* during the run-time would produce the same result. In particular,
311 /// promotion of temporaries must not change program behavior; if the promoted could be
312 /// written to, that would be a problem.
313 struct HasMutInterior;
315 impl Qualif for HasMutInterior {
316 const IDX: usize = 0;
318 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
319 Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
322 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
324 // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
325 // allowed in constants (and the `Checker` will error), and/or it
326 // won't be promoted, due to `&mut ...` or interior mutability.
327 Rvalue::Ref(_, kind, ref place) => {
328 let ty = place.ty(cx.body, cx.tcx).ty;
330 if let BorrowKind::Mut { .. } = kind {
331 // In theory, any zero-sized value could be borrowed
332 // mutably without consequences. However, only &mut []
333 // is allowed right now, and only in functions.
334 if cx.mode == Mode::StaticMut {
335 // Inside a `static mut`, &mut [...] is also allowed.
337 ty::Array(..) | ty::Slice(_) => {}
340 } else if let ty::Array(_, len) = ty.sty {
341 // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
342 // seems unnecessary, given that this is merely a ZST.
343 match len.assert_usize(cx.tcx) {
344 Some(0) if cx.mode == Mode::NonConstFn => {},
353 Rvalue::Aggregate(ref kind, _) => {
354 if let AggregateKind::Adt(def, ..) = **kind {
355 if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
356 let ty = rvalue.ty(cx.body, cx.tcx);
357 assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
366 Self::in_rvalue_structurally(cx, rvalue)
370 /// Constant containing an ADT that implements `Drop`.
371 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
372 /// as that might not be a `const fn`, and (b) because implicit promotion would
373 /// remove side-effects that occur as part of dropping that value.
376 impl Qualif for NeedsDrop {
377 const IDX: usize = 1;
379 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
380 Some(ty.needs_drop(cx.tcx, cx.param_env))
383 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
384 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
385 if let AggregateKind::Adt(def, ..) = **kind {
386 if def.has_dtor(cx.tcx) {
392 Self::in_rvalue_structurally(cx, rvalue)
396 /// Not promotable at all - non-`const fn` calls, `asm!`,
397 /// pointer comparisons, ptr-to-int casts, etc.
398 /// Inside a const context all constness rules apply, so promotion simply has to follow the regular
399 /// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
400 /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
401 /// visitor enforces by emitting errors when working in const context.
402 struct IsNotPromotable;
404 impl Qualif for IsNotPromotable {
405 const IDX: usize = 2;
407 fn in_static(cx: &ConstCx<'_, 'tcx>, static_: &Static<'tcx>) -> bool {
409 StaticKind::Promoted(_) => unreachable!(),
410 StaticKind::Static(def_id) => {
411 // Only allow statics (not consts) to refer to other statics.
412 let allowed = cx.mode == Mode::Static || cx.mode == Mode::StaticMut;
415 cx.tcx.get_attrs(def_id).iter().any(
416 |attr| attr.check_name(sym::thread_local)
422 fn in_projection(cx: &ConstCx<'_, 'tcx>, proj: &Projection<'tcx>) -> bool {
424 ProjectionElem::Deref |
425 ProjectionElem::Downcast(..) => return true,
427 ProjectionElem::ConstantIndex {..} |
428 ProjectionElem::Subslice {..} |
429 ProjectionElem::Index(_) => {}
431 ProjectionElem::Field(..) => {
432 if cx.mode == Mode::NonConstFn {
433 let base_ty = proj.base.ty(cx.body, cx.tcx).ty;
434 if let Some(def) = base_ty.ty_adt_def() {
435 // No promotion of union field accesses.
444 Self::in_projection_structurally(cx, proj)
447 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
449 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
450 let operand_ty = operand.ty(cx.body, cx.tcx);
451 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
452 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
453 match (cast_in, cast_out) {
454 (CastTy::Ptr(_), CastTy::Int(_)) |
455 (CastTy::FnPtr, CastTy::Int(_)) => {
456 // in normal functions, mark such casts as not promotable
463 Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
464 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.body, cx.tcx).sty {
465 assert!(op == BinOp::Eq || op == BinOp::Ne ||
466 op == BinOp::Le || op == BinOp::Lt ||
467 op == BinOp::Ge || op == BinOp::Gt ||
468 op == BinOp::Offset);
470 // raw pointer operations are not allowed inside promoteds
475 Rvalue::NullaryOp(NullOp::Box, _) => return true,
480 Self::in_rvalue_structurally(cx, rvalue)
484 cx: &ConstCx<'_, 'tcx>,
485 callee: &Operand<'tcx>,
486 args: &[Operand<'tcx>],
487 _return_ty: Ty<'tcx>,
489 let fn_ty = callee.ty(cx.body, cx.tcx);
491 ty::FnDef(def_id, _) => {
492 match cx.tcx.fn_sig(def_id).abi() {
494 Abi::PlatformIntrinsic => {
495 assert!(!cx.tcx.is_const_fn(def_id));
496 match &cx.tcx.item_name(def_id).as_str()[..] {
515 | "add_with_overflow"
516 | "sub_with_overflow"
517 | "mul_with_overflow"
528 cx.tcx.is_const_fn(def_id) ||
529 cx.tcx.is_unstable_const_fn(def_id).is_some() ||
530 cx.is_const_panic_fn(def_id);
540 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
544 /// Refers to temporaries which cannot be promoted *implicitly*.
545 /// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
546 /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
547 /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
548 /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
549 struct IsNotImplicitlyPromotable;
551 impl Qualif for IsNotImplicitlyPromotable {
552 const IDX: usize = 3;
555 cx: &ConstCx<'_, 'tcx>,
556 callee: &Operand<'tcx>,
557 args: &[Operand<'tcx>],
558 _return_ty: Ty<'tcx>,
560 if cx.mode == Mode::NonConstFn {
561 if let ty::FnDef(def_id, _) = callee.ty(cx.body, cx.tcx).sty {
562 // Never promote runtime `const fn` calls of
563 // functions without `#[rustc_promotable]`.
564 if !cx.tcx.is_promotable_const_fn(def_id) {
570 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
574 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
575 macro_rules! static_assert_seq_qualifs {
576 ($i:expr => $first:ident $(, $rest:ident)*) => {
578 static_assert_seq_qualifs!($i + 1 => $($rest),*);
584 static_assert!(QUALIF_COUNT == $i);
587 static_assert_seq_qualifs!(
588 0 => HasMutInterior, NeedsDrop, IsNotPromotable, IsNotImplicitlyPromotable
591 impl ConstCx<'_, 'tcx> {
592 fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
593 let mut qualifs = PerQualif::default();
594 qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
595 qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
596 qualifs[IsNotPromotable] = IsNotPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
597 qualifs[IsNotImplicitlyPromotable] =
598 IsNotImplicitlyPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
602 fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
603 let mut qualifs = PerQualif::default();
604 qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
605 qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
606 qualifs[IsNotPromotable] = IsNotPromotable::in_local(self, local);
607 qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_local(self, local);
611 fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
612 let mut qualifs = PerQualif::default();
613 qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
614 qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
615 qualifs[IsNotPromotable] = IsNotPromotable::in_value(self, source);
616 qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_value(self, source);
621 /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
622 /// for value qualifications, and accumulates writes of
623 /// rvalue/call results to locals, in `local_qualif`.
624 /// It also records candidates for promotion in `promotion_candidates`,
625 /// both in functions and const/static items.
626 struct Checker<'a, 'tcx> {
627 cx: ConstCx<'a, 'tcx>,
631 rpo: ReversePostorder<'a, 'tcx>,
633 temp_promotion_state: IndexVec<Local, TempState>,
634 promotion_candidates: Vec<Candidate>,
637 macro_rules! unleash_miri {
639 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
640 $this.tcx.sess.span_warn($this.span, "skipping const checks");
646 impl Deref for Checker<'a, 'tcx> {
647 type Target = ConstCx<'a, 'tcx>;
649 fn deref(&self) -> &Self::Target {
654 impl<'a, 'tcx> Checker<'a, 'tcx> {
655 fn new(tcx: TyCtxt<'tcx, 'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
656 assert!(def_id.is_local());
657 let mut rpo = traversal::reverse_postorder(body);
658 let temps = promote_consts::collect_temps(body, &mut rpo);
661 let param_env = tcx.param_env(def_id);
663 let mut cx = ConstCx {
668 per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
671 for (local, decl) in body.local_decls.iter_enumerated() {
672 if let LocalKind::Arg = body.local_kind(local) {
673 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
674 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
676 per_local.insert(local);
680 if !temps[local].is_promotable() {
681 cx.per_local[IsNotPromotable].insert(local);
683 if let LocalKind::Var = body.local_kind(local) {
684 // Sanity check to prevent implicit and explicit promotion of
686 assert!(cx.per_local[IsNotPromotable].contains(local));
695 temp_promotion_state: temps,
696 promotion_candidates: vec![]
700 // FIXME(eddyb) we could split the errors into meaningful
701 // categories, but enabling full miri would make that
702 // slightly pointless (even with feature-gating).
703 fn not_const(&mut self) {
705 if self.mode.requires_const_checking() {
706 let mut err = struct_span_err!(
710 "{} contains unimplemented expression type",
713 if self.tcx.sess.teach(&err.get_code().unwrap()) {
714 err.note("A function call isn't allowed in the const's initialization expression \
715 because the expression's value must be known at compile-time.");
716 err.note("Remember: you can't use a function call inside a const's initialization \
717 expression! However, you can use it anywhere else.");
723 /// Assigns an rvalue/call qualification to the given destination.
724 fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
725 trace!("assign: {:?} <- {:?}", dest, source);
727 let mut qualifs = self.qualifs_in_value(source);
729 if let ValueSource::Rvalue(&Rvalue::Ref(_, kind, ref place)) = source {
730 // Getting `true` from `HasMutInterior::in_rvalue` means
731 // the borrowed place is disallowed from being borrowed,
732 // due to either a mutable borrow (with some exceptions),
733 // or an shared borrow of a value with interior mutability.
734 // Then `HasMutInterior` is replaced with `IsNotPromotable`,
735 // to avoid duplicate errors (e.g. from reborrowing).
736 if qualifs[HasMutInterior] {
737 qualifs[HasMutInterior] = false;
738 qualifs[IsNotPromotable] = true;
740 if self.mode.requires_const_checking() {
741 if let BorrowKind::Mut { .. } = kind {
742 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
743 "references in {}s may only refer \
744 to immutable values", self.mode);
745 err.span_label(self.span, format!("{}s require immutable values",
747 if self.tcx.sess.teach(&err.get_code().unwrap()) {
748 err.note("References in statics and constants may only refer to \
749 immutable values.\n\n\
750 Statics are shared everywhere, and if they refer to \
751 mutable data one might violate memory safety since \
752 holding multiple mutable references to shared data is \
754 If you really want global mutable state, try using \
755 static mut or a global UnsafeCell.");
759 span_err!(self.tcx.sess, self.span, E0492,
760 "cannot borrow a constant which may contain \
761 interior mutability, create a static instead");
764 } else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
765 // Don't promote BorrowKind::Shallow borrows, as they don't
768 // We might have a candidate for promotion.
769 let candidate = Candidate::Ref(location);
770 // Start by traversing to the "base", with non-deref projections removed.
771 let mut place = place;
772 while let Place::Projection(ref proj) = *place {
773 if proj.elem == ProjectionElem::Deref {
778 debug!("qualify_consts: promotion candidate: place={:?}", place);
779 // We can only promote interior borrows of promotable temps (non-temps
780 // don't get promoted anyway).
781 // (If we bailed out of the loop due to a `Deref` above, we will definitely
782 // not enter the conditional here.)
783 if let Place::Base(PlaceBase::Local(local)) = *place {
784 if self.body.local_kind(local) == LocalKind::Temp {
785 debug!("qualify_consts: promotion candidate: local={:?}", local);
786 // The borrowed place doesn't have `HasMutInterior`
787 // (from `in_rvalue`), so we can safely ignore
788 // `HasMutInterior` from the local's qualifications.
789 // This allows borrowing fields which don't have
790 // `HasMutInterior`, from a type that does, e.g.:
791 // `let _: &'static _ = &(Cell::new(1), 2).1;`
792 let mut local_qualifs = self.qualifs_in_local(local);
793 // Any qualifications, except HasMutInterior (see above), disqualify
795 // This is, in particular, the "implicit promotion" version of
796 // the check making sure that we don't run drop glue during const-eval.
797 local_qualifs[HasMutInterior] = false;
798 if !local_qualifs.0.iter().any(|&qualif| qualif) {
799 debug!("qualify_consts: promotion candidate: {:?}", candidate);
800 self.promotion_candidates.push(candidate);
810 // We treat all locals equal in constants
811 Place::Base(PlaceBase::Local(index)) => break *index,
812 // projections are transparent for assignments
813 // we qualify the entire destination at once, even if just a field would have
814 // stricter qualification
815 Place::Projection(proj) => {
816 // Catch more errors in the destination. `visit_place` also checks various
817 // projection rules like union field access and raw pointer deref
820 PlaceContext::MutatingUse(MutatingUseContext::Store),
825 Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) =>
826 bug!("promoteds don't exist yet during promotion"),
827 Place::Base(PlaceBase::Static(box Static{ kind: _, .. })) => {
828 // Catch more errors in the destination. `visit_place` also checks that we
829 // do not try to access statics from constants or try to mutate statics
832 PlaceContext::MutatingUse(MutatingUseContext::Store),
840 let kind = self.body.local_kind(index);
841 debug!("store to {:?} {:?}", kind, index);
843 // Only handle promotable temps in non-const functions.
844 if self.mode == Mode::NonConstFn {
845 if kind != LocalKind::Temp ||
846 !self.temp_promotion_state[index].is_promotable() {
851 // this is overly restrictive, because even full assignments do not clear the qualif
852 // While we could special case full assignments, this would be inconsistent with
853 // aggregates where we overwrite all fields via assignments, which would not get
855 for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
857 per_local.insert(index);
861 // Ensure the `IsNotPromotable` qualification is preserved.
862 // NOTE(eddyb) this is actually unnecessary right now, as
863 // we never replace the local's qualif, but we might in
864 // the future, and so it serves to catch changes that unset
865 // important bits (in which case, asserting `contains` could
866 // be replaced with calling `insert` to re-set the bit).
867 if kind == LocalKind::Temp {
868 if !self.temp_promotion_state[index].is_promotable() {
869 assert!(self.cx.per_local[IsNotPromotable].contains(index));
874 /// Check a whole const, static initializer or const fn.
875 fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
876 debug!("const-checking {} {:?}", self.mode, self.def_id);
878 let body = self.body;
880 let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
881 let mut bb = START_BLOCK;
883 seen_blocks.insert(bb.index());
885 self.visit_basic_block_data(bb, &body[bb]);
887 let target = match body[bb].terminator().kind {
888 TerminatorKind::Goto { target } |
889 TerminatorKind::Drop { target, .. } |
890 TerminatorKind::Assert { target, .. } |
891 TerminatorKind::Call { destination: Some((_, target)), .. } => {
895 // Non-terminating calls cannot produce any value.
896 TerminatorKind::Call { destination: None, .. } => {
900 TerminatorKind::SwitchInt {..} |
901 TerminatorKind::DropAndReplace { .. } |
902 TerminatorKind::Resume |
903 TerminatorKind::Abort |
904 TerminatorKind::GeneratorDrop |
905 TerminatorKind::Yield { .. } |
906 TerminatorKind::Unreachable |
907 TerminatorKind::FalseEdges { .. } |
908 TerminatorKind::FalseUnwind { .. } => None,
910 TerminatorKind::Return => {
917 Some(target) if !seen_blocks.contains(target.index()) => {
928 // Collect all the temps we need to promote.
929 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
931 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
932 for candidate in &self.promotion_candidates {
934 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
935 match self.body[bb].statements[stmt_idx].kind {
936 StatementKind::Assign(
938 box Rvalue::Ref(_, _, Place::Base(PlaceBase::Local(index)))
940 promoted_temps.insert(index);
945 Candidate::Argument { .. } => {}
949 let mut qualifs = self.qualifs_in_local(RETURN_PLACE);
951 // Account for errors in consts by using the
952 // conservative type qualification instead.
953 if qualifs[IsNotPromotable] {
954 qualifs = self.qualifs_in_any_value_of_ty(body.return_ty());
957 (qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
961 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
964 place_base: &PlaceBase<'tcx>,
965 context: PlaceContext,
968 self.super_place_base(place_base, context, location);
970 PlaceBase::Local(_) => {}
971 PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) => {
974 PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => {
978 .any(|attr| attr.check_name(sym::thread_local)) {
979 if self.mode.requires_const_checking() {
980 span_err!(self.tcx.sess, self.span, E0625,
981 "thread-local statics cannot be \
982 accessed at compile-time");
987 // Only allow statics (not consts) to refer to other statics.
988 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
989 if self.mode == Mode::Static && context.is_mutating_use() {
990 // this is not strictly necessary as miri will also bail out
991 // For interior mutability we can't really catch this statically as that
992 // goes through raw pointers and intermediate temporaries, so miri has
993 // to catch this anyway
994 self.tcx.sess.span_err(
996 "cannot mutate statics in the initializer of another static",
1001 unleash_miri!(self);
1003 if self.mode.requires_const_checking() {
1004 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
1005 "{}s cannot refer to statics, use \
1006 a constant instead", self.mode);
1007 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1009 "Static and const variables can refer to other const variables. \
1010 But a const variable cannot refer to a static variable."
1013 "To fix this, the value can be extracted as a const and then used."
1022 fn visit_projection(
1024 proj: &Projection<'tcx>,
1025 context: PlaceContext,
1029 "visit_place_projection: proj={:?} context={:?} location={:?}",
1030 proj, context, location,
1032 self.super_projection(proj, context, location);
1034 ProjectionElem::Deref => {
1035 if context.is_mutating_use() {
1036 // `not_const` errors out in const contexts
1039 let base_ty = proj.base.ty(self.body, self.tcx).ty;
1041 Mode::NonConstFn => {},
1043 if let ty::RawPtr(_) = base_ty.sty {
1044 if !self.tcx.features().const_raw_ptr_deref {
1046 &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
1047 self.span, GateIssue::Language,
1049 "dereferencing raw pointers in {}s is unstable",
1059 ProjectionElem::ConstantIndex {..} |
1060 ProjectionElem::Subslice {..} |
1061 ProjectionElem::Field(..) |
1062 ProjectionElem::Index(_) => {
1063 let base_ty = proj.base.ty(self.body, self.tcx).ty;
1064 if let Some(def) = base_ty.ty_adt_def() {
1068 if !self.tcx.features().const_fn_union {
1070 &self.tcx.sess.parse_sess, sym::const_fn_union,
1071 self.span, GateIssue::Language,
1072 "unions in const fn are unstable",
1087 ProjectionElem::Downcast(..) => {
1093 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
1094 debug!("visit_operand: operand={:?} location={:?}", operand, location);
1095 self.super_operand(operand, location);
1098 Operand::Move(ref place) => {
1099 // Mark the consumed locals to indicate later drops are noops.
1100 if let Place::Base(PlaceBase::Local(local)) = *place {
1101 self.cx.per_local[NeedsDrop].remove(local);
1105 Operand::Constant(_) => {}
1109 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1110 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
1112 // Check nested operands and places.
1113 if let Rvalue::Ref(_, kind, ref place) = *rvalue {
1114 // Special-case reborrows.
1115 let mut reborrow_place = None;
1116 if let Place::Projection(ref proj) = *place {
1117 if let ProjectionElem::Deref = proj.elem {
1118 let base_ty = proj.base.ty(self.body, self.tcx).ty;
1119 if let ty::Ref(..) = base_ty.sty {
1120 reborrow_place = Some(&proj.base);
1125 if let Some(place) = reborrow_place {
1126 let ctx = match kind {
1127 BorrowKind::Shared => PlaceContext::NonMutatingUse(
1128 NonMutatingUseContext::SharedBorrow,
1130 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
1131 NonMutatingUseContext::ShallowBorrow,
1133 BorrowKind::Unique => PlaceContext::NonMutatingUse(
1134 NonMutatingUseContext::UniqueBorrow,
1136 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
1137 MutatingUseContext::Borrow,
1140 self.visit_place(place, ctx, location);
1142 self.super_rvalue(rvalue, location);
1145 self.super_rvalue(rvalue, location);
1150 Rvalue::Repeat(..) |
1151 Rvalue::UnaryOp(UnOp::Neg, _) |
1152 Rvalue::UnaryOp(UnOp::Not, _) |
1153 Rvalue::NullaryOp(NullOp::SizeOf, _) |
1154 Rvalue::CheckedBinaryOp(..) |
1155 Rvalue::Cast(CastKind::Pointer(_), ..) |
1156 Rvalue::Discriminant(..) |
1159 Rvalue::Aggregate(..) => {}
1161 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
1162 let operand_ty = operand.ty(self.body, self.tcx);
1163 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
1164 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
1165 match (cast_in, cast_out) {
1166 (CastTy::Ptr(_), CastTy::Int(_)) |
1167 (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
1168 unleash_miri!(self);
1169 if !self.tcx.features().const_raw_ptr_to_usize_cast {
1170 // in const fn and constants require the feature gate
1171 // FIXME: make it unsafe inside const fn and constants
1173 &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
1174 self.span, GateIssue::Language,
1176 "casting pointers to integers in {}s is unstable",
1186 Rvalue::BinaryOp(op, ref lhs, _) => {
1187 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).sty {
1188 assert!(op == BinOp::Eq || op == BinOp::Ne ||
1189 op == BinOp::Le || op == BinOp::Lt ||
1190 op == BinOp::Ge || op == BinOp::Gt ||
1191 op == BinOp::Offset);
1193 unleash_miri!(self);
1194 if self.mode.requires_const_checking() &&
1195 !self.tcx.features().const_compare_raw_pointers
1197 // require the feature gate inside constants and const fn
1198 // FIXME: make it unsafe to use these operations
1200 &self.tcx.sess.parse_sess,
1201 sym::const_compare_raw_pointers,
1203 GateIssue::Language,
1204 &format!("comparing raw pointers inside {}", self.mode),
1210 Rvalue::NullaryOp(NullOp::Box, _) => {
1211 unleash_miri!(self);
1212 if self.mode.requires_const_checking() {
1213 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1214 "allocations are not allowed in {}s", self.mode);
1215 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1216 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1218 "The value of statics and constants must be known at compile time, \
1219 and they live for the entire lifetime of a program. Creating a boxed \
1220 value allocates memory on the heap at runtime, and therefore cannot \
1221 be done at compile time."
1230 fn visit_terminator_kind(&mut self,
1231 kind: &TerminatorKind<'tcx>,
1232 location: Location) {
1233 debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
1234 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1235 if let Some((ref dest, _)) = *destination {
1236 self.assign(dest, ValueSource::Call {
1239 return_ty: dest.ty(self.body, self.tcx).ty,
1243 let fn_ty = func.ty(self.body, self.tcx);
1244 let mut callee_def_id = None;
1245 let mut is_shuffle = false;
1247 ty::FnDef(def_id, _) => {
1248 callee_def_id = Some(def_id);
1249 match self.tcx.fn_sig(def_id).abi() {
1250 Abi::RustIntrinsic |
1251 Abi::PlatformIntrinsic => {
1252 assert!(!self.tcx.is_const_fn(def_id));
1253 match &self.tcx.item_name(def_id).as_str()[..] {
1254 // special intrinsic that can be called diretly without an intrinsic
1255 // feature gate needs a language feature gate
1257 if self.mode.requires_const_checking() {
1258 // const eval transmute calls only with the feature gate
1259 if !self.tcx.features().const_transmute {
1261 &self.tcx.sess.parse_sess, sym::const_transmute,
1262 self.span, GateIssue::Language,
1263 &format!("The use of std::mem::transmute() \
1264 is gated in {}s", self.mode));
1269 name if name.starts_with("simd_shuffle") => {
1273 // no need to check feature gates, intrinsics are only callable
1274 // from the libstd or with forever unstable feature gates
1279 // In normal functions no calls are feature-gated.
1280 if self.mode.requires_const_checking() {
1281 let unleash_miri = self
1286 .unleash_the_miri_inside_of_you;
1287 if self.tcx.is_const_fn(def_id) || unleash_miri {
1288 // stable const fns or unstable const fns
1289 // with their feature gate active
1290 // FIXME(eddyb) move stability checks from `is_const_fn` here.
1291 } else if self.is_const_panic_fn(def_id) {
1292 // Check the const_panic feature gate.
1293 // FIXME: cannot allow this inside `allow_internal_unstable`
1294 // because that would make `panic!` insta stable in constants,
1295 // since the macro is marked with the attribute.
1296 if !self.tcx.features().const_panic {
1297 // Don't allow panics in constants without the feature gate.
1299 &self.tcx.sess.parse_sess,
1302 GateIssue::Language,
1303 &format!("panicking in {}s is unstable", self.mode),
1306 } else if let Some(feature)
1307 = self.tcx.is_unstable_const_fn(def_id) {
1308 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1309 // functions without the feature gate active in this crate in
1310 // order to report a better error message than the one below.
1311 if !self.span.allows_unstable(feature) {
1312 let mut err = self.tcx.sess.struct_span_err(self.span,
1313 &format!("`{}` is not yet stable as a const fn",
1314 self.tcx.def_path_str(def_id)));
1315 if nightly_options::is_nightly_build() {
1317 "add `#![feature({})]` to the \
1318 crate attributes to enable",
1324 let mut err = struct_span_err!(
1328 "calls in {}s are limited to constant functions, \
1329 tuple structs and tuple variants",
1339 if self.mode.requires_const_checking() {
1340 let mut err = self.tcx.sess.struct_span_err(
1342 &format!("function pointers are not allowed in const fn"));
1351 // No need to do anything in constants and statics, as everything is "constant" anyway
1352 // so promotion would be useless.
1353 if self.mode != Mode::Static && self.mode != Mode::Const {
1354 let constant_args = callee_def_id.and_then(|id| {
1355 args_required_const(self.tcx, id)
1356 }).unwrap_or_default();
1357 for (i, arg) in args.iter().enumerate() {
1358 if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
1362 let candidate = Candidate::Argument { bb: location.block, index: i };
1363 // Since the argument is required to be constant,
1364 // we care about constness, not promotability.
1365 // If we checked for promotability, we'd miss out on
1366 // the results of function calls (which are never promoted
1367 // in runtime code).
1368 // This is not a problem, because the argument explicitly
1369 // requests constness, in contrast to regular promotion
1370 // which happens even without the user requesting it.
1371 // We can error out with a hard error if the argument is not
1373 if !IsNotPromotable::in_operand(self, arg) {
1374 debug!("visit_terminator_kind: candidate={:?}", candidate);
1375 self.promotion_candidates.push(candidate);
1378 span_err!(self.tcx.sess, self.span, E0526,
1379 "shuffle indices are not constant");
1381 self.tcx.sess.span_err(self.span,
1382 &format!("argument {} is required to be a constant",
1389 // Check callee and argument operands.
1390 self.visit_operand(func, location);
1392 self.visit_operand(arg, location);
1394 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1395 self.super_terminator_kind(kind, location);
1397 // Deny *any* live drops anywhere other than functions.
1398 if self.mode.requires_const_checking() {
1399 unleash_miri!(self);
1400 // HACK(eddyb): emulate a bit of dataflow analysis,
1401 // conservatively, that drop elaboration will do.
1402 let needs_drop = if let Place::Base(PlaceBase::Local(local)) = *place {
1403 if NeedsDrop::in_local(self, local) {
1404 Some(self.body.local_decls[local].source_info.span)
1412 if let Some(span) = needs_drop {
1413 // Double-check the type being dropped, to minimize false positives.
1414 let ty = place.ty(self.body, self.tcx).ty;
1415 if ty.needs_drop(self.tcx, self.param_env) {
1416 struct_span_err!(self.tcx.sess, span, E0493,
1417 "destructors cannot be evaluated at compile-time")
1418 .span_label(span, format!("{}s cannot evaluate destructors",
1425 // Qualify any operands inside other terminators.
1426 self.super_terminator_kind(kind, location);
1430 fn visit_assign(&mut self,
1432 rvalue: &Rvalue<'tcx>,
1433 location: Location) {
1434 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1435 self.assign(dest, ValueSource::Rvalue(rvalue), location);
1437 self.visit_rvalue(rvalue, location);
1440 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1441 debug!("visit_source_info: source_info={:?}", source_info);
1442 self.span = source_info.span;
1445 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1446 debug!("visit_statement: statement={:?} location={:?}", statement, location);
1447 match statement.kind {
1448 StatementKind::Assign(..) => {
1449 self.super_statement(statement, location);
1451 StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
1454 // FIXME(eddyb) should these really do nothing?
1455 StatementKind::FakeRead(..) |
1456 StatementKind::SetDiscriminant { .. } |
1457 StatementKind::StorageLive(_) |
1458 StatementKind::StorageDead(_) |
1459 StatementKind::InlineAsm {..} |
1460 StatementKind::Retag { .. } |
1461 StatementKind::AscribeUserType(..) |
1462 StatementKind::Nop => {}
1467 pub fn provide(providers: &mut Providers<'_>) {
1468 *providers = Providers {
1474 fn mir_const_qualif<'tcx>(tcx: TyCtxt<'tcx, 'tcx>, def_id: DefId) -> (u8, &'tcx BitSet<Local>) {
1475 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1476 // cannot yet be stolen), because `mir_validated()`, which steals
1477 // from `mir_const(), forces this query to execute before
1478 // performing the steal.
1479 let body = &tcx.mir_const(def_id).borrow();
1481 if body.return_ty().references_error() {
1482 tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
1483 return (1 << IsNotPromotable::IDX, tcx.arena.alloc(BitSet::new_empty(0)));
1486 Checker::new(tcx, def_id, body, Mode::Const).check_const()
1489 pub struct QualifyAndPromoteConstants;
1491 impl MirPass for QualifyAndPromoteConstants {
1492 fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx, 'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
1493 // There's not really any point in promoting errorful MIR.
1494 if body.return_ty().references_error() {
1495 tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
1499 if src.promoted.is_some() {
1503 let def_id = src.def_id();
1504 let id = tcx.hir().as_local_hir_id(def_id).unwrap();
1505 let mut const_promoted_temps = None;
1506 let mode = match tcx.hir().body_owner_kind_by_hir_id(id) {
1507 hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1508 hir::BodyOwnerKind::Fn => {
1509 if tcx.is_const_fn(def_id) {
1515 hir::BodyOwnerKind::Const => {
1516 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1519 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1520 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1523 debug!("run_pass: mode={:?}", mode);
1524 if mode == Mode::NonConstFn || mode == Mode::ConstFn {
1525 // This is ugly because Checker holds onto mir,
1526 // which can't be mutated until its scope ends.
1527 let (temps, candidates) = {
1528 let mut checker = Checker::new(tcx, def_id, body, mode);
1529 if mode == Mode::ConstFn {
1530 if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
1531 checker.check_const();
1532 } else if tcx.is_min_const_fn(def_id) {
1533 // enforce `min_const_fn` for stable const fns
1534 use super::qualify_min_const_fn::is_min_const_fn;
1535 if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1536 let mut diag = struct_span_err!(
1543 diag.note("for more information, see issue \
1544 https://github.com/rust-lang/rust/issues/57563");
1546 "add #![feature(const_fn)] to the crate attributes to enable",
1550 // this should not produce any errors, but better safe than sorry
1552 checker.check_const();
1555 // Enforce a constant-like CFG for `const fn`.
1556 checker.check_const();
1559 while let Some((bb, data)) = checker.rpo.next() {
1560 checker.visit_basic_block_data(bb, data);
1564 (checker.temp_promotion_state, checker.promotion_candidates)
1567 // Do the actual promotion, now that we know what's viable.
1568 promote_consts::promote_candidates(body, tcx, temps, candidates);
1570 if !body.control_flow_destroyed.is_empty() {
1571 let mut locals = body.vars_iter();
1572 if let Some(local) = locals.next() {
1573 let span = body.local_decls[local].source_info.span;
1574 let mut error = tcx.sess.struct_span_err(
1577 "new features like let bindings are not permitted in {}s \
1578 which also use short circuiting operators",
1582 for (span, kind) in body.control_flow_destroyed.iter() {
1585 &format!("use of {} here does not actually short circuit due to \
1586 the const evaluator presently not being able to do control flow. \
1587 See https://github.com/rust-lang/rust/issues/49146 for more \
1588 information.", kind),
1591 for local in locals {
1592 let span = body.local_decls[local].source_info.span;
1595 "more locals defined here",
1601 let promoted_temps = if mode == Mode::Const {
1602 // Already computed by `mir_const_qualif`.
1603 const_promoted_temps.unwrap()
1605 Checker::new(tcx, def_id, body, mode).check_const().1
1608 // In `const` and `static` everything without `StorageDead`
1609 // is `'static`, we don't have to create promoted MIR fragments,
1610 // just remove `Drop` and `StorageDead` on "promoted" locals.
1611 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1612 for block in body.basic_blocks_mut() {
1613 block.statements.retain(|statement| {
1614 match statement.kind {
1615 StatementKind::StorageDead(index) => {
1616 !promoted_temps.contains(index)
1621 let terminator = block.terminator_mut();
1622 match terminator.kind {
1623 TerminatorKind::Drop {
1624 location: Place::Base(PlaceBase::Local(index)),
1628 if promoted_temps.contains(index) {
1629 terminator.kind = TerminatorKind::Goto {
1639 // Statics must be Sync.
1640 if mode == Mode::Static {
1641 // `#[thread_local]` statics don't have to be `Sync`.
1642 for attr in &tcx.get_attrs(def_id)[..] {
1643 if attr.check_name(sym::thread_local) {
1647 let ty = body.return_ty();
1648 tcx.infer_ctxt().enter(|infcx| {
1649 let param_env = ty::ParamEnv::empty();
1650 let cause = traits::ObligationCause::new(body.span, id, traits::SharedStatic);
1651 let mut fulfillment_cx = traits::FulfillmentContext::new();
1652 fulfillment_cx.register_bound(&infcx,
1655 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1657 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1658 infcx.report_fulfillment_errors(&err, None, false);
1665 fn args_required_const(tcx: TyCtxt<'_, '_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1666 let attrs = tcx.get_attrs(def_id);
1667 let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
1668 let mut ret = FxHashSet::default();
1669 for meta in attr.meta_item_list()? {
1670 match meta.literal()?.node {
1671 LitKind::Int(a, _) => { ret.insert(a as usize); }