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_index::bit_set::BitSet;
8 use rustc_index::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};
37 use crate::transform::check_consts::ops::{self, NonConstOp};
39 /// What kind of item we are in.
40 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
44 /// A `static mut` item.
46 /// A `const fn` item.
48 /// A `const` item or an anonymous constant (e.g. in array lengths).
50 /// Other type of `fn`.
55 /// Determine whether we have to do full const-checking because syntactically, we
56 /// are required to be "const".
58 fn requires_const_checking(self) -> bool {
59 self != Mode::NonConstFn
63 impl fmt::Display for Mode {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 Mode::Const => write!(f, "constant"),
67 Mode::Static | Mode::StaticMut => write!(f, "static"),
68 Mode::ConstFn => write!(f, "constant function"),
69 Mode::NonConstFn => write!(f, "function")
74 const QUALIF_COUNT: usize = 4;
76 // FIXME(eddyb) once we can use const generics, replace this array with
77 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
78 #[derive(Copy, Clone, Default)]
79 struct PerQualif<T>([T; QUALIF_COUNT]);
81 impl<T: Clone> PerQualif<T> {
82 fn new(x: T) -> Self {
83 PerQualif([x.clone(), x.clone(), x.clone(), x])
87 impl<T> PerQualif<T> {
88 fn as_mut(&mut self) -> PerQualif<&mut T> {
89 let [x0, x1, x2, x3] = &mut self.0;
90 PerQualif([x0, x1, x2, x3])
93 fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
94 let [x0, x1, x2, x3] = self.0;
95 let [y0, y1, y2, y3] = other.0;
96 PerQualif([(x0, y0), (x1, y1), (x2, y2), (x3, y3)])
100 impl PerQualif<bool> {
101 fn encode_to_bits(self) -> u8 {
102 self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
103 bits | ((qualif as u8) << i)
107 fn decode_from_bits(bits: u8) -> Self {
108 let mut qualifs = Self::default();
109 for (i, qualif) in qualifs.0.iter_mut().enumerate() {
110 *qualif = (bits & (1 << i)) != 0;
116 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
119 fn index(&self, _: Q) -> &T {
124 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
125 fn index_mut(&mut self, _: Q) -> &mut T {
130 struct ConstCx<'a, 'tcx> {
132 param_env: ty::ParamEnv<'tcx>,
134 body: &'a Body<'tcx>,
136 per_local: PerQualif<BitSet<Local>>,
139 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
140 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
141 Some(def_id) == self.tcx.lang_items().panic_fn() ||
142 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
146 #[derive(Copy, Clone, Debug)]
147 enum ValueSource<'a, 'tcx> {
148 Rvalue(&'a Rvalue<'tcx>),
149 DropAndReplace(&'a Operand<'tcx>),
151 callee: &'a Operand<'tcx>,
152 args: &'a [Operand<'tcx>],
157 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
158 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
159 /// "I found something bad, no reason to go on searching". `false` is only returned if we
160 /// definitely cannot find anything bad anywhere.
162 /// The default implementations proceed structurally.
166 /// Return the qualification that is (conservatively) correct for any value
167 /// of the type, or `None` if the qualification is not value/type-based.
168 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
172 /// Return a mask for the qualification, given a type. This is `false` iff
173 /// no value of that type can have the qualification.
174 fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
175 Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
178 fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
179 cx.per_local.0[Self::IDX].contains(local)
182 fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
183 // FIXME(eddyb) should we do anything here for value properties?
187 fn in_projection_structurally(
188 cx: &ConstCx<'_, 'tcx>,
189 place: PlaceRef<'_, 'tcx>,
191 if let [proj_base @ .., elem] = place.projection {
192 let base_qualif = Self::in_place(cx, PlaceRef {
194 projection: proj_base,
196 let qualif = base_qualif && Self::mask_for_ty(
198 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
199 .projection_ty(cx.tcx, elem)
203 ProjectionElem::Deref |
204 ProjectionElem::Subslice { .. } |
205 ProjectionElem::Field(..) |
206 ProjectionElem::ConstantIndex { .. } |
207 ProjectionElem::Downcast(..) => qualif,
209 // FIXME(eddyb) shouldn't this be masked *after* including the
210 // index local? Then again, it's `usize` which is neither
211 // `HasMutInterior` nor `NeedsDrop`.
212 ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
215 bug!("This should be called if projection is not empty");
220 cx: &ConstCx<'_, 'tcx>,
221 place: PlaceRef<'_, 'tcx>,
223 Self::in_projection_structurally(cx, place)
226 fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
229 base: PlaceBase::Local(local),
231 } => Self::in_local(cx, *local),
233 base: PlaceBase::Static(box Static {
234 kind: StaticKind::Promoted(..),
238 } => bug!("qualifying already promoted MIR"),
240 base: PlaceBase::Static(static_),
243 Self::in_static(cx, static_)
248 } => Self::in_projection(cx, place),
252 fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
254 Operand::Copy(ref place) |
255 Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
257 Operand::Constant(ref constant) => {
258 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
259 // Don't peek inside trait associated constants.
260 if cx.tcx.trait_of_item(def_id).is_some() {
261 Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
263 let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
265 let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
267 // Just in case the type is more specific than
268 // the definition, e.g., impl associated const
269 // with type parameters, take it into account.
270 qualif && Self::mask_for_ty(cx, constant.literal.ty)
279 fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
281 Rvalue::NullaryOp(..) => false,
283 Rvalue::Discriminant(ref place) |
284 Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
286 Rvalue::Use(ref operand) |
287 Rvalue::Repeat(ref operand, _) |
288 Rvalue::UnaryOp(_, ref operand) |
289 Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
291 Rvalue::BinaryOp(_, ref lhs, ref rhs) |
292 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
293 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
296 Rvalue::Ref(_, _, ref place) => {
297 // Special-case reborrows to be more like a copy of the reference.
298 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
299 if ProjectionElem::Deref == elem {
300 let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
301 if let ty::Ref(..) = base_ty.kind {
302 return Self::in_place(cx, PlaceRef {
304 projection: proj_base,
310 Self::in_place(cx, place.as_ref())
313 Rvalue::Aggregate(_, ref operands) => {
314 operands.iter().any(|o| Self::in_operand(cx, o))
319 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
320 Self::in_rvalue_structurally(cx, rvalue)
324 cx: &ConstCx<'_, 'tcx>,
325 _callee: &Operand<'tcx>,
326 _args: &[Operand<'tcx>],
329 // Be conservative about the returned value of a const fn.
330 Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
333 fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
335 ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
336 ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
337 ValueSource::Call { callee, args, return_ty } => {
338 Self::in_call(cx, callee, args, return_ty)
344 /// Constant containing interior mutability (`UnsafeCell<T>`).
345 /// This must be ruled out to make sure that evaluating the constant at compile-time
346 /// and at *any point* during the run-time would produce the same result. In particular,
347 /// promotion of temporaries must not change program behavior; if the promoted could be
348 /// written to, that would be a problem.
349 struct HasMutInterior;
351 impl Qualif for HasMutInterior {
352 const IDX: usize = 0;
354 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
355 Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
358 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
360 // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
361 // allowed in constants (and the `Checker` will error), and/or it
362 // won't be promoted, due to `&mut ...` or interior mutability.
363 Rvalue::Ref(_, kind, ref place) => {
364 let ty = place.ty(cx.body, cx.tcx).ty;
366 if let BorrowKind::Mut { .. } = kind {
367 // In theory, any zero-sized value could be borrowed
368 // mutably without consequences. However, only &mut []
369 // is allowed right now, and only in functions.
370 if cx.mode == Mode::StaticMut {
371 // Inside a `static mut`, &mut [...] is also allowed.
373 ty::Array(..) | ty::Slice(_) => {}
376 } else if let ty::Array(_, len) = ty.kind {
377 // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
378 // seems unnecessary, given that this is merely a ZST.
379 match len.try_eval_usize(cx.tcx, cx.param_env) {
380 Some(0) if cx.mode == Mode::NonConstFn => {},
389 Rvalue::Aggregate(ref kind, _) => {
390 if let AggregateKind::Adt(def, ..) = **kind {
391 if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
392 let ty = rvalue.ty(cx.body, cx.tcx);
393 assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
402 Self::in_rvalue_structurally(cx, rvalue)
406 /// Constant containing an ADT that implements `Drop`.
407 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
408 /// as that might not be a `const fn`, and (b) because implicit promotion would
409 /// remove side-effects that occur as part of dropping that value.
412 impl Qualif for NeedsDrop {
413 const IDX: usize = 1;
415 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
416 Some(ty.needs_drop(cx.tcx, cx.param_env))
419 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
420 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
421 if let AggregateKind::Adt(def, ..) = **kind {
422 if def.has_dtor(cx.tcx) {
428 Self::in_rvalue_structurally(cx, rvalue)
432 /// Not promotable at all - non-`const fn` calls, `asm!`,
433 /// pointer comparisons, ptr-to-int casts, etc.
434 /// Inside a const context all constness rules apply, so promotion simply has to follow the regular
435 /// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
436 /// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
437 /// visitor enforces by emitting errors when working in const context.
438 struct IsNotPromotable;
440 impl Qualif for IsNotPromotable {
441 const IDX: usize = 2;
443 fn in_static(cx: &ConstCx<'_, 'tcx>, static_: &Static<'tcx>) -> bool {
445 StaticKind::Promoted(_, _) => unreachable!(),
446 StaticKind::Static => {
447 // Only allow statics (not consts) to refer to other statics.
448 // FIXME(eddyb) does this matter at all for promotion?
449 let allowed = cx.mode == Mode::Static || cx.mode == Mode::StaticMut;
452 cx.tcx.get_attrs(static_.def_id).iter().any(
453 |attr| attr.check_name(sym::thread_local)
460 cx: &ConstCx<'_, 'tcx>,
461 place: PlaceRef<'_, 'tcx>,
463 if let [proj_base @ .., elem] = place.projection {
465 ProjectionElem::Deref |
466 ProjectionElem::Downcast(..) => return true,
468 ProjectionElem::ConstantIndex {..} |
469 ProjectionElem::Subslice {..} |
470 ProjectionElem::Index(_) => {}
472 ProjectionElem::Field(..) => {
473 if cx.mode == Mode::NonConstFn {
474 let base_ty = Place::ty_from(place.base, proj_base, cx.body, cx.tcx).ty;
475 if let Some(def) = base_ty.ty_adt_def() {
476 // No promotion of union field accesses.
485 Self::in_projection_structurally(cx, place)
487 bug!("This should be called if projection is not empty");
491 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
493 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
494 let operand_ty = operand.ty(cx.body, cx.tcx);
495 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
496 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
497 match (cast_in, cast_out) {
498 (CastTy::Ptr(_), CastTy::Int(_)) |
499 (CastTy::FnPtr, CastTy::Int(_)) => {
500 // in normal functions, mark such casts as not promotable
507 Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
508 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.body, cx.tcx).kind {
509 assert!(op == BinOp::Eq || op == BinOp::Ne ||
510 op == BinOp::Le || op == BinOp::Lt ||
511 op == BinOp::Ge || op == BinOp::Gt ||
512 op == BinOp::Offset);
514 // raw pointer operations are not allowed inside promoteds
519 Rvalue::NullaryOp(NullOp::Box, _) => return true,
524 Self::in_rvalue_structurally(cx, rvalue)
528 cx: &ConstCx<'_, 'tcx>,
529 callee: &Operand<'tcx>,
530 args: &[Operand<'tcx>],
531 _return_ty: Ty<'tcx>,
533 let fn_ty = callee.ty(cx.body, cx.tcx);
535 ty::FnDef(def_id, _) => {
536 match cx.tcx.fn_sig(def_id).abi() {
538 Abi::PlatformIntrinsic => {
539 assert!(!cx.tcx.is_const_fn(def_id));
540 match &cx.tcx.item_name(def_id).as_str()[..] {
559 | "add_with_overflow"
560 | "sub_with_overflow"
561 | "mul_with_overflow"
574 cx.tcx.is_const_fn(def_id) ||
575 cx.tcx.is_unstable_const_fn(def_id).is_some() ||
576 cx.is_const_panic_fn(def_id);
586 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
590 /// Refers to temporaries which cannot be promoted *implicitly*.
591 /// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
592 /// Implicit promotion has almost the same rules, except that disallows `const fn` except for
593 /// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
594 /// into a failing compile-time operation e.g. due to addresses being compared inside the function.
595 struct IsNotImplicitlyPromotable;
597 impl Qualif for IsNotImplicitlyPromotable {
598 const IDX: usize = 3;
601 cx: &ConstCx<'_, 'tcx>,
602 callee: &Operand<'tcx>,
603 args: &[Operand<'tcx>],
604 _return_ty: Ty<'tcx>,
606 if cx.mode == Mode::NonConstFn {
607 if let ty::FnDef(def_id, _) = callee.ty(cx.body, cx.tcx).kind {
608 // Never promote runtime `const fn` calls of
609 // functions without `#[rustc_promotable]`.
610 if !cx.tcx.is_promotable_const_fn(def_id) {
616 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
620 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
621 macro_rules! static_assert_seq_qualifs {
622 ($i:expr => $first:ident $(, $rest:ident)*) => {
624 static_assert_seq_qualifs!($i + 1 => $($rest),*);
630 static_assert!(QUALIF_COUNT == $i);
633 static_assert_seq_qualifs!(
634 0 => HasMutInterior, NeedsDrop, IsNotPromotable, IsNotImplicitlyPromotable
637 impl ConstCx<'_, 'tcx> {
638 fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
639 let mut qualifs = PerQualif::default();
640 qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
641 qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
642 qualifs[IsNotPromotable] = IsNotPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
643 qualifs[IsNotImplicitlyPromotable] =
644 IsNotImplicitlyPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
648 fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
649 let mut qualifs = PerQualif::default();
650 qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
651 qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
652 qualifs[IsNotPromotable] = IsNotPromotable::in_local(self, local);
653 qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_local(self, local);
657 fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
658 let mut qualifs = PerQualif::default();
659 qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
660 qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
661 qualifs[IsNotPromotable] = IsNotPromotable::in_value(self, source);
662 qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_value(self, source);
667 /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
668 /// for value qualifications, and accumulates writes of
669 /// rvalue/call results to locals, in `local_qualif`.
670 /// It also records candidates for promotion in `promotion_candidates`,
671 /// both in functions and const/static items.
672 struct Checker<'a, 'tcx> {
673 cx: ConstCx<'a, 'tcx>,
677 rpo: ReversePostorder<'a, 'tcx>,
679 temp_promotion_state: IndexVec<Local, TempState>,
680 promotion_candidates: Vec<Candidate>,
681 unchecked_promotion_candidates: Vec<Candidate>,
683 /// If `true`, do not emit errors to the user, merely collect them in `errors`.
684 suppress_errors: bool,
685 errors: Vec<(Span, String)>,
688 macro_rules! unleash_miri {
690 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
691 if $this.mode.requires_const_checking() && !$this.suppress_errors {
692 $this.tcx.sess.span_warn($this.span, "skipping const checks");
699 impl Deref for Checker<'a, 'tcx> {
700 type Target = ConstCx<'a, 'tcx>;
702 fn deref(&self) -> &Self::Target {
707 impl<'a, 'tcx> Checker<'a, 'tcx> {
708 fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
709 assert!(def_id.is_local());
710 let mut rpo = traversal::reverse_postorder(body);
711 let (temps, unchecked_promotion_candidates) =
712 promote_consts::collect_temps_and_candidates(tcx, body, &mut rpo);
715 let param_env = tcx.param_env(def_id);
717 let mut cx = ConstCx {
722 per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
725 for (local, decl) in body.local_decls.iter_enumerated() {
726 if let LocalKind::Arg = body.local_kind(local) {
727 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
728 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
730 per_local.insert(local);
734 if !temps[local].is_promotable() {
735 cx.per_local[IsNotPromotable].insert(local);
737 if let LocalKind::Var = body.local_kind(local) {
738 // Sanity check to prevent implicit and explicit promotion of
740 assert!(cx.per_local[IsNotPromotable].contains(local));
749 temp_promotion_state: temps,
750 promotion_candidates: vec![],
751 unchecked_promotion_candidates,
753 suppress_errors: false,
757 // FIXME(eddyb) we could split the errors into meaningful
758 // categories, but enabling full miri would make that
759 // slightly pointless (even with feature-gating).
760 fn not_const(&mut self, op: impl NonConstOp) {
762 if self.mode.requires_const_checking() && !self.suppress_errors {
763 self.record_error(op);
764 let mut err = struct_span_err!(
768 "{} contains unimplemented expression type",
771 if self.tcx.sess.teach(&err.get_code().unwrap()) {
772 err.note("A function call isn't allowed in the const's initialization expression \
773 because the expression's value must be known at compile-time.");
774 err.note("Remember: you can't use a function call inside a const's initialization \
775 expression! However, you can use it anywhere else.");
781 fn record_error(&mut self, op: impl NonConstOp) {
782 self.record_error_spanned(op, self.span);
785 fn record_error_spanned(&mut self, op: impl NonConstOp, span: Span) {
786 self.errors.push((span, format!("{:?}", op)));
789 /// Assigns an rvalue/call qualification to the given destination.
790 fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
791 trace!("assign: {:?} <- {:?}", dest, source);
793 let mut qualifs = self.qualifs_in_value(source);
796 ValueSource::Rvalue(&Rvalue::Ref(_, kind, ref place)) => {
797 // Getting `true` from `HasMutInterior::in_rvalue` means
798 // the borrowed place is disallowed from being borrowed,
799 // due to either a mutable borrow (with some exceptions),
800 // or an shared borrow of a value with interior mutability.
801 // Then `HasMutInterior` is replaced with `IsNotPromotable`,
802 // to avoid duplicate errors (e.g. from reborrowing).
803 if qualifs[HasMutInterior] {
804 qualifs[HasMutInterior] = false;
805 qualifs[IsNotPromotable] = true;
807 debug!("suppress_errors: {}", self.suppress_errors);
808 if self.mode.requires_const_checking() && !self.suppress_errors {
809 if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
810 self.record_error(ops::MutBorrow(kind));
811 if let BorrowKind::Mut { .. } = kind {
812 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
813 "references in {}s may only refer \
814 to immutable values", self.mode);
815 err.span_label(self.span, format!("{}s require immutable values",
817 if self.tcx.sess.teach(&err.get_code().unwrap()) {
818 err.note("References in statics and constants may only refer \
819 to immutable values.\n\n\
820 Statics are shared everywhere, and if they refer to \
821 mutable data one might violate memory safety since \
822 holding multiple mutable references to shared data \
824 If you really want global mutable state, try using \
825 static mut or a global UnsafeCell.");
829 span_err!(self.tcx.sess, self.span, E0492,
830 "cannot borrow a constant which may contain \
831 interior mutability, create a static instead");
835 } else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
836 // Don't promote BorrowKind::Shallow borrows, as they don't
838 // FIXME(eddyb) the two other kinds of borrow (`Shallow` and `Unique`)
839 // aren't promoted here but *could* be promoted as part of a larger
840 // value because `IsNotPromotable` isn't being set for them,
841 // need to figure out what is the intended behavior.
843 // We might have a candidate for promotion.
844 let candidate = Candidate::Ref(location);
845 // Start by traversing to the "base", with non-deref projections removed.
847 place.projection.iter().rev().find(|&elem| *elem == ProjectionElem::Deref);
850 "qualify_consts: promotion candidate: place={:?} {:?}",
851 place.base, deref_proj
853 // We can only promote interior borrows of promotable temps (non-temps
854 // don't get promoted anyway).
855 // (If we bailed out of the loop due to a `Deref` above, we will definitely
856 // not enter the conditional here.)
857 if let (PlaceBase::Local(local), None) = (&place.base, deref_proj) {
858 if self.body.local_kind(*local) == LocalKind::Temp {
859 debug!("qualify_consts: promotion candidate: local={:?}", local);
860 // The borrowed place doesn't have `HasMutInterior`
861 // (from `in_rvalue`), so we can safely ignore
862 // `HasMutInterior` from the local's qualifications.
863 // This allows borrowing fields which don't have
864 // `HasMutInterior`, from a type that does, e.g.:
865 // `let _: &'static _ = &(Cell::new(1), 2).1;`
866 let mut local_qualifs = self.qualifs_in_local(*local);
867 // Any qualifications, except HasMutInterior (see above), disqualify
869 // This is, in particular, the "implicit promotion" version of
870 // the check making sure that we don't run drop glue during const-eval.
871 local_qualifs[HasMutInterior] = false;
872 if !local_qualifs.0.iter().any(|&qualif| qualif) {
873 debug!("qualify_consts: promotion candidate: {:?}", candidate);
874 self.promotion_candidates.push(candidate);
880 ValueSource::Rvalue(&Rvalue::Repeat(ref operand, _)) => {
881 debug!("assign: self.cx.mode={:?} self.def_id={:?} location={:?} operand={:?}",
882 self.cx.mode, self.def_id, location, operand);
883 if self.should_promote_repeat_expression(operand) &&
884 self.tcx.features().const_in_array_repeat_expressions {
885 self.promotion_candidates.push(Candidate::Repeat(location));
891 let mut dest_projection = &dest.projection[..];
893 match (&dest.base, dest_projection) {
894 // We treat all locals equal in constants
895 (&PlaceBase::Local(index), []) => break index,
896 // projections are transparent for assignments
897 // we qualify the entire destination at once, even if just a field would have
898 // stricter qualification
899 (base, [proj_base @ .., _]) => {
900 // Catch more errors in the destination. `visit_place` also checks various
901 // projection rules like union field access and raw pointer deref
902 let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
903 self.visit_place_base(base, context, location);
904 self.visit_projection(base, dest_projection, context, location);
905 dest_projection = proj_base;
907 (&PlaceBase::Static(box Static {
908 kind: StaticKind::Promoted(..),
910 }), []) => bug!("promoteds don't exist yet during promotion"),
911 (&PlaceBase::Static(box Static{ kind: _, .. }), []) => {
912 // Catch more errors in the destination. `visit_place` also checks that we
913 // do not try to access statics from constants or try to mutate statics
914 let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
915 self.visit_place_base(&dest.base, context, location);
921 let kind = self.body.local_kind(index);
922 debug!("store to {:?} {:?}", kind, index);
924 // Only handle promotable temps in non-const functions.
925 if self.mode == Mode::NonConstFn {
926 if kind != LocalKind::Temp ||
927 !self.temp_promotion_state[index].is_promotable() {
932 // this is overly restrictive, because even full assignments do not clear the qualif
933 // While we could special case full assignments, this would be inconsistent with
934 // aggregates where we overwrite all fields via assignments, which would not get
936 for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
938 per_local.insert(index);
942 // Ensure the `IsNotPromotable` qualification is preserved.
943 // NOTE(eddyb) this is actually unnecessary right now, as
944 // we never replace the local's qualif, but we might in
945 // the future, and so it serves to catch changes that unset
946 // important bits (in which case, asserting `contains` could
947 // be replaced with calling `insert` to re-set the bit).
948 if kind == LocalKind::Temp {
949 if !self.temp_promotion_state[index].is_promotable() {
950 assert!(self.cx.per_local[IsNotPromotable].contains(index));
955 /// Check a whole const, static initializer or const fn.
956 fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
957 use crate::transform::check_consts as new_checker;
959 debug!("const-checking {} {:?}", self.mode, self.def_id);
961 // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are
963 let use_new_validator = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
964 if use_new_validator {
965 debug!("Using dataflow-based const validator");
968 let item = new_checker::Item::new(self.tcx, self.def_id, self.body);
969 let mut validator = new_checker::validation::Validator::new(&item);
971 validator.suppress_errors = !use_new_validator;
972 self.suppress_errors = use_new_validator;
974 let body = self.body;
976 let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
977 let mut bb = START_BLOCK;
978 let mut has_controlflow_error = false;
980 seen_blocks.insert(bb.index());
982 self.visit_basic_block_data(bb, &body[bb]);
983 validator.visit_basic_block_data(bb, &body[bb]);
985 let target = match body[bb].terminator().kind {
986 TerminatorKind::Goto { target } |
987 TerminatorKind::FalseUnwind { real_target: target, .. } |
988 TerminatorKind::Drop { target, .. } |
989 TerminatorKind::DropAndReplace { target, .. } |
990 TerminatorKind::Assert { target, .. } |
991 TerminatorKind::Call { destination: Some((_, target)), .. } => {
995 // Non-terminating calls cannot produce any value.
996 TerminatorKind::Call { destination: None, .. } => {
1000 TerminatorKind::SwitchInt {..} |
1001 TerminatorKind::Resume |
1002 TerminatorKind::Abort |
1003 TerminatorKind::GeneratorDrop |
1004 TerminatorKind::Yield { .. } |
1005 TerminatorKind::Unreachable |
1006 TerminatorKind::FalseEdges { .. } => None,
1008 TerminatorKind::Return => {
1014 // No loops allowed.
1015 Some(target) if !seen_blocks.contains(target.index()) => {
1019 has_controlflow_error = true;
1020 self.not_const(ops::Loop);
1021 validator.check_op(ops::Loop);
1027 // The new validation pass should agree with the old when running on simple const bodies
1028 // (e.g. no `if` or `loop`).
1029 if !use_new_validator {
1030 let mut new_errors = validator.take_errors();
1032 // FIXME: each checker sometimes emits the same error with the same span twice in a row.
1033 self.errors.dedup();
1036 if self.errors != new_errors {
1040 std::mem::replace(&mut self.errors, vec![]),
1046 // Collect all the temps we need to promote.
1047 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
1049 // HACK(eddyb) don't try to validate promotion candidates if any
1050 // parts of the control-flow graph were skipped due to an error.
1051 let promotion_candidates = if has_controlflow_error {
1052 let unleash_miri = self
1057 .unleash_the_miri_inside_of_you;
1059 self.tcx.sess.delay_span_bug(
1061 "check_const: expected control-flow error(s)",
1064 self.promotion_candidates.clone()
1066 self.valid_promotion_candidates()
1068 debug!("qualify_const: promotion_candidates={:?}", promotion_candidates);
1069 for candidate in promotion_candidates {
1071 Candidate::Repeat(Location { block: bb, statement_index: stmt_idx }) => {
1072 if let StatementKind::Assign(box(_, Rvalue::Repeat(
1073 Operand::Move(place),
1075 ))) = &self.body[bb].statements[stmt_idx].kind {
1076 if let Some(index) = place.as_local() {
1077 promoted_temps.insert(index);
1081 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
1082 if let StatementKind::Assign(
1085 Rvalue::Ref(_, _, place)
1087 ) = &self.body[bb].statements[stmt_idx].kind {
1088 if let Some(index) = place.as_local() {
1089 promoted_temps.insert(index);
1093 Candidate::Argument { .. } => {}
1097 let mut qualifs = self.qualifs_in_local(RETURN_PLACE);
1099 // Account for errors in consts by using the
1100 // conservative type qualification instead.
1101 if qualifs[IsNotPromotable] {
1102 qualifs = self.qualifs_in_any_value_of_ty(body.return_ty());
1105 (qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
1108 /// Get the subset of `unchecked_promotion_candidates` that are eligible
1110 // FIXME(eddyb) replace the old candidate gathering with this.
1111 fn valid_promotion_candidates(&self) -> Vec<Candidate> {
1112 // Sanity-check the promotion candidates.
1113 let candidates = promote_consts::validate_candidates(
1117 &self.temp_promotion_state,
1118 &self.unchecked_promotion_candidates,
1121 if candidates != self.promotion_candidates {
1122 let report = |msg, candidate| {
1123 let span = match candidate {
1124 Candidate::Ref(loc) |
1125 Candidate::Repeat(loc) => self.body.source_info(loc).span,
1126 Candidate::Argument { bb, .. } => {
1127 self.body[bb].terminator().source_info.span
1130 self.tcx.sess.span_err(span, &format!("{}: {:?}", msg, candidate));
1133 for &c in &self.promotion_candidates {
1134 if !candidates.contains(&c) {
1135 report("invalidated old candidate", c);
1139 for &c in &candidates {
1140 if !self.promotion_candidates.contains(&c) {
1141 report("extra new candidate", c);
1145 bug!("promotion candidate validation mismatches (see above)");
1151 /// Returns `true` if the operand of a repeat expression is promotable.
1152 fn should_promote_repeat_expression(&self, operand: &Operand<'tcx>) -> bool {
1153 let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) ||
1154 IsNotPromotable::in_operand(self, operand);
1155 debug!("should_promote_repeat_expression: operand={:?} not_promotable={:?}",
1156 operand, not_promotable);
1161 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
1162 fn visit_place_base(
1164 place_base: &PlaceBase<'tcx>,
1165 context: PlaceContext,
1168 self.super_place_base(place_base, context, location);
1170 PlaceBase::Local(_) => {}
1171 PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
1174 PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
1178 .any(|attr| attr.check_name(sym::thread_local)) {
1179 if self.mode.requires_const_checking() && !self.suppress_errors {
1180 self.record_error(ops::ThreadLocalAccess);
1181 span_err!(self.tcx.sess, self.span, E0625,
1182 "thread-local statics cannot be \
1183 accessed at compile-time");
1188 // Only allow statics (not consts) to refer to other statics.
1189 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
1190 if self.mode == Mode::Static
1191 && context.is_mutating_use()
1192 && !self.suppress_errors
1194 // this is not strictly necessary as miri will also bail out
1195 // For interior mutability we can't really catch this statically as that
1196 // goes through raw pointers and intermediate temporaries, so miri has
1197 // to catch this anyway
1198 self.tcx.sess.span_err(
1200 "cannot mutate statics in the initializer of another static",
1205 unleash_miri!(self);
1207 if self.mode.requires_const_checking() && !self.suppress_errors {
1208 self.record_error(ops::StaticAccess);
1209 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
1210 "{}s cannot refer to statics, use \
1211 a constant instead", self.mode);
1212 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1214 "Static and const variables can refer to other const variables. \
1215 But a const variable cannot refer to a static variable."
1218 "To fix this, the value can be extracted as a const and then used."
1227 fn visit_projection_elem(
1229 place_base: &PlaceBase<'tcx>,
1230 proj_base: &[PlaceElem<'tcx>],
1231 elem: &PlaceElem<'tcx>,
1232 context: PlaceContext,
1236 "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \
1237 context={:?} location={:?}",
1245 self.super_projection_elem(place_base, proj_base, elem, context, location);
1248 ProjectionElem::Deref => {
1249 if context.is_mutating_use() {
1250 // `not_const` errors out in const contexts
1251 self.not_const(ops::MutDeref)
1253 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
1255 Mode::NonConstFn => {}
1256 _ if self.suppress_errors => {}
1258 if let ty::RawPtr(_) = base_ty.kind {
1259 if !self.tcx.features().const_raw_ptr_deref {
1260 self.record_error(ops::RawPtrDeref);
1262 &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
1263 self.span, GateIssue::Language,
1265 "dereferencing raw pointers in {}s is unstable",
1275 ProjectionElem::ConstantIndex {..} |
1276 ProjectionElem::Subslice {..} |
1277 ProjectionElem::Field(..) |
1278 ProjectionElem::Index(_) => {
1279 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
1280 if let Some(def) = base_ty.ty_adt_def() {
1284 if !self.tcx.features().const_fn_union
1285 && !self.suppress_errors
1287 self.record_error(ops::UnionAccess);
1289 &self.tcx.sess.parse_sess, sym::const_fn_union,
1290 self.span, GateIssue::Language,
1291 "unions in const fn are unstable",
1306 ProjectionElem::Downcast(..) => {
1307 self.not_const(ops::Downcast)
1312 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
1313 debug!("visit_operand: operand={:?} location={:?}", operand, location);
1314 self.super_operand(operand, location);
1317 Operand::Move(ref place) => {
1318 // Mark the consumed locals to indicate later drops are noops.
1319 if let Some(local) = place.as_local() {
1320 self.cx.per_local[NeedsDrop].remove(local);
1324 Operand::Constant(_) => {}
1328 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1329 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
1331 // Check nested operands and places.
1332 if let Rvalue::Ref(_, kind, ref place) = *rvalue {
1333 // Special-case reborrows.
1334 let mut reborrow_place = None;
1335 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
1336 if elem == ProjectionElem::Deref {
1337 let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
1338 if let ty::Ref(..) = base_ty.kind {
1339 reborrow_place = Some(proj_base);
1344 if let Some(proj) = reborrow_place {
1345 let ctx = match kind {
1346 BorrowKind::Shared => PlaceContext::NonMutatingUse(
1347 NonMutatingUseContext::SharedBorrow,
1349 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
1350 NonMutatingUseContext::ShallowBorrow,
1352 BorrowKind::Unique => PlaceContext::NonMutatingUse(
1353 NonMutatingUseContext::UniqueBorrow,
1355 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
1356 MutatingUseContext::Borrow,
1359 self.visit_place_base(&place.base, ctx, location);
1360 self.visit_projection(&place.base, proj, ctx, location);
1362 self.super_rvalue(rvalue, location);
1365 self.super_rvalue(rvalue, location);
1370 Rvalue::Repeat(..) |
1371 Rvalue::UnaryOp(UnOp::Neg, _) |
1372 Rvalue::UnaryOp(UnOp::Not, _) |
1373 Rvalue::NullaryOp(NullOp::SizeOf, _) |
1374 Rvalue::CheckedBinaryOp(..) |
1375 Rvalue::Cast(CastKind::Pointer(_), ..) |
1376 Rvalue::Discriminant(..) |
1379 Rvalue::Aggregate(..) => {}
1381 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
1382 let operand_ty = operand.ty(self.body, self.tcx);
1383 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
1384 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
1385 match (cast_in, cast_out) {
1386 (CastTy::Ptr(_), CastTy::Int(_)) |
1387 (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
1388 unleash_miri!(self);
1389 if !self.tcx.features().const_raw_ptr_to_usize_cast
1390 && !self.suppress_errors
1392 // in const fn and constants require the feature gate
1393 // FIXME: make it unsafe inside const fn and constants
1394 self.record_error(ops::RawPtrToIntCast);
1396 &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
1397 self.span, GateIssue::Language,
1399 "casting pointers to integers in {}s is unstable",
1409 Rvalue::BinaryOp(op, ref lhs, _) => {
1410 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
1411 assert!(op == BinOp::Eq || op == BinOp::Ne ||
1412 op == BinOp::Le || op == BinOp::Lt ||
1413 op == BinOp::Ge || op == BinOp::Gt ||
1414 op == BinOp::Offset);
1416 unleash_miri!(self);
1417 if self.mode.requires_const_checking() &&
1418 !self.tcx.features().const_compare_raw_pointers &&
1419 !self.suppress_errors
1421 self.record_error(ops::RawPtrComparison);
1422 // require the feature gate inside constants and const fn
1423 // FIXME: make it unsafe to use these operations
1425 &self.tcx.sess.parse_sess,
1426 sym::const_compare_raw_pointers,
1428 GateIssue::Language,
1429 &format!("comparing raw pointers inside {}", self.mode),
1435 Rvalue::NullaryOp(NullOp::Box, _) => {
1436 unleash_miri!(self);
1437 if self.mode.requires_const_checking() && !self.suppress_errors {
1438 self.record_error(ops::HeapAllocation);
1439 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1440 "allocations are not allowed in {}s", self.mode);
1441 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1442 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1444 "The value of statics and constants must be known at compile time, \
1445 and they live for the entire lifetime of a program. Creating a boxed \
1446 value allocates memory on the heap at runtime, and therefore cannot \
1447 be done at compile time."
1456 fn visit_terminator_kind(&mut self,
1457 kind: &TerminatorKind<'tcx>,
1458 location: Location) {
1459 debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
1460 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1461 if let Some((ref dest, _)) = *destination {
1462 self.assign(dest, ValueSource::Call {
1465 return_ty: dest.ty(self.body, self.tcx).ty,
1469 let fn_ty = func.ty(self.body, self.tcx);
1470 let mut callee_def_id = None;
1471 let mut is_shuffle = false;
1473 ty::FnDef(def_id, _) => {
1474 callee_def_id = Some(def_id);
1475 match self.tcx.fn_sig(def_id).abi() {
1476 Abi::RustIntrinsic |
1477 Abi::PlatformIntrinsic => {
1478 assert!(!self.tcx.is_const_fn(def_id));
1479 match &self.tcx.item_name(def_id).as_str()[..] {
1480 // special intrinsic that can be called diretly without an intrinsic
1481 // feature gate needs a language feature gate
1483 if self.mode.requires_const_checking()
1484 && !self.suppress_errors
1486 // const eval transmute calls only with the feature gate
1487 if !self.tcx.features().const_transmute {
1488 self.record_error(ops::Transmute);
1490 &self.tcx.sess.parse_sess, sym::const_transmute,
1491 self.span, GateIssue::Language,
1492 &format!("The use of std::mem::transmute() \
1493 is gated in {}s", self.mode));
1498 name if name.starts_with("simd_shuffle") => {
1502 // no need to check feature gates, intrinsics are only callable
1503 // from the libstd or with forever unstable feature gates
1508 // In normal functions no calls are feature-gated.
1509 if self.mode.requires_const_checking() {
1510 let unleash_miri = self
1515 .unleash_the_miri_inside_of_you;
1516 if self.tcx.is_const_fn(def_id)
1518 || self.suppress_errors
1520 // stable const fns or unstable const fns
1521 // with their feature gate active
1522 // FIXME(eddyb) move stability checks from `is_const_fn` here.
1523 } else if self.is_const_panic_fn(def_id) {
1524 // Check the const_panic feature gate.
1525 // FIXME: cannot allow this inside `allow_internal_unstable`
1526 // because that would make `panic!` insta stable in constants,
1527 // since the macro is marked with the attribute.
1528 if !self.tcx.features().const_panic {
1529 // Don't allow panics in constants without the feature gate.
1530 self.record_error(ops::Panic);
1532 &self.tcx.sess.parse_sess,
1535 GateIssue::Language,
1536 &format!("panicking in {}s is unstable", self.mode),
1539 } else if let Some(feature)
1540 = self.tcx.is_unstable_const_fn(def_id) {
1541 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1542 // functions without the feature gate active in this crate in
1543 // order to report a better error message than the one below.
1544 if !self.span.allows_unstable(feature) {
1545 self.record_error(ops::FnCallUnstable(def_id, feature));
1546 let mut err = self.tcx.sess.struct_span_err(self.span,
1547 &format!("`{}` is not yet stable as a const fn",
1548 self.tcx.def_path_str(def_id)));
1549 if nightly_options::is_nightly_build() {
1551 "add `#![feature({})]` to the \
1552 crate attributes to enable",
1558 self.record_error(ops::FnCallNonConst(def_id));
1559 let mut err = struct_span_err!(
1563 "calls in {}s are limited to constant functions, \
1564 tuple structs and tuple variants",
1574 unleash_miri!(self);
1575 if self.mode.requires_const_checking() && !self.suppress_errors {
1576 self.record_error(ops::FnCallIndirect);
1577 let mut err = self.tcx.sess.struct_span_err(
1579 "function pointers are not allowed in const fn"
1585 self.not_const(ops::FnCallOther);
1589 // No need to do anything in constants and statics, as everything is "constant" anyway
1590 // so promotion would be useless.
1591 if self.mode != Mode::Static && self.mode != Mode::Const {
1592 let constant_args = callee_def_id.and_then(|id| {
1593 args_required_const(self.tcx, id)
1594 }).unwrap_or_default();
1595 for (i, arg) in args.iter().enumerate() {
1596 if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
1600 let candidate = Candidate::Argument { bb: location.block, index: i };
1601 // Since the argument is required to be constant,
1602 // we care about constness, not promotability.
1603 // If we checked for promotability, we'd miss out on
1604 // the results of function calls (which are never promoted
1605 // in runtime code).
1606 // This is not a problem, because the argument explicitly
1607 // requests constness, in contrast to regular promotion
1608 // which happens even without the user requesting it.
1610 // `promote_consts` is responsible for emitting the error if
1611 // the argument is not promotable.
1612 if !IsNotPromotable::in_operand(self, arg) {
1613 debug!("visit_terminator_kind: candidate={:?}", candidate);
1614 self.promotion_candidates.push(candidate);
1619 // Check callee and argument operands.
1620 self.visit_operand(func, location);
1622 self.visit_operand(arg, location);
1624 } else if let TerminatorKind::Drop {
1625 location: ref place, ..
1626 } | TerminatorKind::DropAndReplace {
1627 location: ref place, ..
1630 TerminatorKind::DropAndReplace { .. } => {}
1631 _ => self.super_terminator_kind(kind, location),
1634 // Deny *any* live drops anywhere other than functions.
1635 if self.mode.requires_const_checking() && !self.suppress_errors {
1636 unleash_miri!(self);
1637 // HACK(eddyb): emulate a bit of dataflow analysis,
1638 // conservatively, that drop elaboration will do.
1639 let needs_drop = if let Some(local) = place.as_local() {
1640 if NeedsDrop::in_local(self, local) {
1641 Some(self.body.local_decls[local].source_info.span)
1649 if let Some(span) = needs_drop {
1650 // Double-check the type being dropped, to minimize false positives.
1651 let ty = place.ty(self.body, self.tcx).ty;
1652 if ty.needs_drop(self.tcx, self.param_env) {
1653 self.record_error_spanned(ops::LiveDrop, span);
1654 struct_span_err!(self.tcx.sess, span, E0493,
1655 "destructors cannot be evaluated at compile-time")
1656 .span_label(span, format!("{}s cannot evaluate destructors",
1664 TerminatorKind::DropAndReplace { ref value, .. } => {
1665 self.assign(place, ValueSource::DropAndReplace(value), location);
1666 self.visit_operand(value, location);
1671 // Qualify any operands inside other terminators.
1672 self.super_terminator_kind(kind, location);
1676 fn visit_assign(&mut self,
1678 rvalue: &Rvalue<'tcx>,
1679 location: Location) {
1680 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1681 self.assign(dest, ValueSource::Rvalue(rvalue), location);
1683 self.visit_rvalue(rvalue, location);
1686 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1687 debug!("visit_source_info: source_info={:?}", source_info);
1688 self.span = source_info.span;
1691 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1692 debug!("visit_statement: statement={:?} location={:?}", statement, location);
1693 match statement.kind {
1694 StatementKind::Assign(..) => {
1695 self.super_statement(statement, location);
1697 StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
1698 self.not_const(ops::IfOrMatch);
1700 // FIXME(eddyb) should these really do nothing?
1701 StatementKind::FakeRead(..) |
1702 StatementKind::SetDiscriminant { .. } |
1703 StatementKind::StorageLive(_) |
1704 StatementKind::StorageDead(_) |
1705 StatementKind::InlineAsm {..} |
1706 StatementKind::Retag { .. } |
1707 StatementKind::AscribeUserType(..) |
1708 StatementKind::Nop => {}
1713 pub fn provide(providers: &mut Providers<'_>) {
1714 *providers = Providers {
1720 // FIXME(eddyb) this is only left around for the validation logic
1721 // in `promote_consts`, see the comment in `validate_operand`.
1722 pub(super) const QUALIF_ERROR_BIT: u8 = 1 << IsNotPromotable::IDX;
1724 fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
1725 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1726 // cannot yet be stolen), because `mir_validated()`, which steals
1727 // from `mir_const(), forces this query to execute before
1728 // performing the steal.
1729 let body = &tcx.mir_const(def_id).borrow();
1731 if body.return_ty().references_error() {
1732 tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
1733 return (QUALIF_ERROR_BIT, tcx.arena.alloc(BitSet::new_empty(0)));
1736 Checker::new(tcx, def_id, body, Mode::Const).check_const()
1739 pub struct QualifyAndPromoteConstants<'tcx> {
1740 pub promoted: Cell<IndexVec<Promoted, Body<'tcx>>>,
1743 impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> {
1744 fn default() -> Self {
1745 QualifyAndPromoteConstants {
1746 promoted: Cell::new(IndexVec::new()),
1751 impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
1752 fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
1753 // There's not really any point in promoting errorful MIR.
1754 if body.return_ty().references_error() {
1755 tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
1759 if src.promoted.is_some() {
1763 let def_id = src.def_id();
1764 let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1766 let mode = determine_mode(tcx, hir_id, def_id);
1768 debug!("run_pass: mode={:?}", mode);
1769 if let Mode::NonConstFn | Mode::ConstFn = mode {
1770 // This is ugly because Checker holds onto mir,
1771 // which can't be mutated until its scope ends.
1772 let (temps, candidates) = {
1773 let mut checker = Checker::new(tcx, def_id, body, mode);
1774 if let Mode::ConstFn = mode {
1775 let use_min_const_fn_checks =
1776 !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
1777 tcx.is_min_const_fn(def_id);
1778 if use_min_const_fn_checks {
1779 // Enforce `min_const_fn` for stable `const fn`s.
1780 use super::qualify_min_const_fn::is_min_const_fn;
1781 if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1782 error_min_const_fn_violation(tcx, span, err);
1786 // `check_const` should not produce any errors, but better safe than sorry
1788 // NOTE(eddyb) `check_const` is actually needed for promotion inside
1789 // `min_const_fn` functions.
1792 // Enforce a constant-like CFG for `const fn`.
1793 checker.check_const();
1795 while let Some((bb, data)) = checker.rpo.next() {
1796 checker.visit_basic_block_data(bb, data);
1800 let promotion_candidates = checker.valid_promotion_candidates();
1801 (checker.temp_promotion_state, promotion_candidates)
1804 // Do the actual promotion, now that we know what's viable.
1806 promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
1809 check_short_circuiting_in_const_local(tcx, body, mode);
1811 let promoted_temps = match mode {
1812 Mode::Const => tcx.mir_const_qualif(def_id).1,
1813 _ => Checker::new(tcx, def_id, body, mode).check_const().1,
1815 remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
1818 if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
1819 // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
1820 check_static_is_sync(tcx, body, hir_id);
1825 fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
1826 match tcx.hir().body_owner_kind(hir_id) {
1827 hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1828 hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
1829 hir::BodyOwnerKind::Fn => Mode::NonConstFn,
1830 hir::BodyOwnerKind::Const => Mode::Const,
1831 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1832 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1836 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
1837 struct_span_err!(tcx.sess, span, E0723, "{}", msg)
1838 .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
1839 .help("add `#![feature(const_fn)]` to the crate attributes to enable")
1843 fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
1844 if body.control_flow_destroyed.is_empty() {
1848 let mut locals = body.vars_iter();
1849 if let Some(local) = locals.next() {
1850 let span = body.local_decls[local].source_info.span;
1851 let mut error = tcx.sess.struct_span_err(
1854 "new features like let bindings are not permitted in {}s \
1855 which also use short circuiting operators",
1859 for (span, kind) in body.control_flow_destroyed.iter() {
1862 &format!("use of {} here does not actually short circuit due to \
1863 the const evaluator presently not being able to do control flow. \
1864 See https://github.com/rust-lang/rust/issues/49146 for more \
1865 information.", kind),
1868 for local in locals {
1869 let span = body.local_decls[local].source_info.span;
1870 error.span_note(span, "more locals defined here");
1876 /// In `const` and `static` everything without `StorageDead`
1877 /// is `'static`, we don't have to create promoted MIR fragments,
1878 /// just remove `Drop` and `StorageDead` on "promoted" locals.
1879 fn remove_drop_and_storage_dead_on_promoted_locals(
1880 body: &mut Body<'tcx>,
1881 promoted_temps: &BitSet<Local>,
1883 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1885 for block in body.basic_blocks_mut() {
1886 block.statements.retain(|statement| {
1887 match statement.kind {
1888 StatementKind::StorageDead(index) => !promoted_temps.contains(index),
1892 let terminator = block.terminator_mut();
1893 match &terminator.kind {
1894 TerminatorKind::Drop {
1899 if let Some(index) = location.as_local() {
1900 if promoted_temps.contains(index) {
1901 terminator.kind = TerminatorKind::Goto { target: *target };
1910 fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
1911 let ty = body.return_ty();
1912 tcx.infer_ctxt().enter(|infcx| {
1913 let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1914 let mut fulfillment_cx = traits::FulfillmentContext::new();
1915 let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
1916 fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1917 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1918 infcx.report_fulfillment_errors(&err, None, false);
1923 fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1924 let attrs = tcx.get_attrs(def_id);
1925 let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
1926 let mut ret = FxHashSet::default();
1927 for meta in attr.meta_item_list()? {
1928 match meta.literal()?.kind {
1929 LitKind::Int(a, _) => { ret.insert(a as usize); }
1936 fn validator_mismatch(
1939 mut old_errors: Vec<(Span, String)>,
1940 mut new_errors: Vec<(Span, String)>,
1942 error!("old validator: {:?}", old_errors);
1943 error!("new validator: {:?}", new_errors);
1945 // ICE on nightly if the validators do not emit exactly the same errors.
1946 // Users can supress this panic with an unstable compiler flag (hopefully after
1947 // filing an issue).
1948 let opts = &tcx.sess.opts;
1949 let strict_validation_enabled = opts.unstable_features.is_nightly_build()
1950 && !opts.debugging_opts.suppress_const_validation_back_compat_ice;
1952 if !strict_validation_enabled {
1956 // If this difference would cause a regression from the old to the new or vice versa, trigger
1958 if old_errors.is_empty() || new_errors.is_empty() {
1959 span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1962 // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the
1963 // new validator to be more conservative about when a dropped local has been moved out of.
1965 // Supress the mismatch ICE in cases where the validators disagree only on the number of
1966 // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s.
1968 let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop");
1969 let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow");
1971 let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect();
1972 let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect();
1974 let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors;
1976 let old_mut_borrows = old_errors.iter().filter(is_mut_borrow);
1977 let new_mut_borrows = new_errors.iter().filter(is_mut_borrow);
1979 let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some();
1981 if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) {
1985 span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1988 const VALIDATOR_MISMATCH_ERR: &str =
1989 r"Disagreement between legacy and dataflow-based const validators.
1990 After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code.";