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_target::spec::abi::Abi;
11 use rustc::hir::def_id::DefId;
12 use rustc::traits::{self, TraitEngine};
13 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
14 use rustc::ty::cast::CastTy;
15 use rustc::ty::query::Providers;
17 use rustc::mir::interpret::ConstValue;
18 use rustc::mir::traversal::ReversePostorder;
19 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
20 use rustc::middle::lang_items;
21 use rustc::session::config::nightly_options;
22 use syntax::feature_gate::{emit_feature_err, GateIssue};
23 use syntax::symbol::sym;
24 use syntax_pos::{Span, DUMMY_SP};
29 use std::ops::{Deref, Index, IndexMut};
32 use rustc::hir::HirId;
33 use crate::transform::{MirPass, MirSource};
34 use super::promote_consts::{self, Candidate, TempState};
35 use crate::transform::check_consts::ops::{self, NonConstOp};
37 /// What kind of item we are in.
38 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
42 /// A `static mut` item.
44 /// A `const fn` item.
46 /// A `const` item or an anonymous constant (e.g. in array lengths).
48 /// Other type of `fn`.
53 /// Determine whether we have to do full const-checking because syntactically, we
54 /// are required to be "const".
56 fn requires_const_checking(self) -> bool {
57 self != Mode::NonConstFn
61 impl fmt::Display for Mode {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 Mode::Const => write!(f, "constant"),
65 Mode::Static | Mode::StaticMut => write!(f, "static"),
66 Mode::ConstFn => write!(f, "constant function"),
67 Mode::NonConstFn => write!(f, "function")
72 const QUALIF_COUNT: usize = 2;
74 // FIXME(eddyb) once we can use const generics, replace this array with
75 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
76 #[derive(Copy, Clone, Default)]
77 struct PerQualif<T>([T; QUALIF_COUNT]);
79 impl<T: Clone> PerQualif<T> {
80 fn new(x: T) -> Self {
81 PerQualif([x.clone(), x])
85 impl<T> PerQualif<T> {
86 fn as_mut(&mut self) -> PerQualif<&mut T> {
87 let [x0, x1] = &mut self.0;
91 fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
92 let [x0, x1] = self.0;
93 let [y0, y1] = other.0;
94 PerQualif([(x0, y0), (x1, y1)])
98 impl PerQualif<bool> {
99 fn encode_to_bits(self) -> u8 {
100 self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
101 bits | ((qualif as u8) << i)
105 fn decode_from_bits(bits: u8) -> Self {
106 let mut qualifs = Self::default();
107 for (i, qualif) in qualifs.0.iter_mut().enumerate() {
108 *qualif = (bits & (1 << i)) != 0;
114 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
117 fn index(&self, _: Q) -> &T {
122 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
123 fn index_mut(&mut self, _: Q) -> &mut T {
128 struct ConstCx<'a, 'tcx> {
130 param_env: ty::ParamEnv<'tcx>,
132 body: &'a Body<'tcx>,
134 per_local: PerQualif<BitSet<Local>>,
137 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
138 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
139 Some(def_id) == self.tcx.lang_items().panic_fn() ||
140 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
144 #[derive(Copy, Clone, Debug)]
145 enum ValueSource<'a, 'tcx> {
146 Rvalue(&'a Rvalue<'tcx>),
147 DropAndReplace(&'a Operand<'tcx>),
149 callee: &'a Operand<'tcx>,
150 args: &'a [Operand<'tcx>],
155 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
156 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
157 /// "I found something bad, no reason to go on searching". `false` is only returned if we
158 /// definitely cannot find anything bad anywhere.
160 /// The default implementations proceed structurally.
164 /// Return the qualification that is (conservatively) correct for any value
165 /// of the type, or `None` if the qualification is not value/type-based.
166 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
170 /// Return a mask for the qualification, given a type. This is `false` iff
171 /// no value of that type can have the qualification.
172 fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
173 Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
176 fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
177 cx.per_local.0[Self::IDX].contains(local)
180 fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
181 // FIXME(eddyb) should we do anything here for value properties?
185 fn in_projection_structurally(
186 cx: &ConstCx<'_, 'tcx>,
187 place: PlaceRef<'_, 'tcx>,
189 if let [proj_base @ .., elem] = place.projection {
190 let base_qualif = Self::in_place(cx, PlaceRef {
192 projection: proj_base,
194 let qualif = base_qualif && Self::mask_for_ty(
196 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
197 .projection_ty(cx.tcx, elem)
201 ProjectionElem::Deref |
202 ProjectionElem::Subslice { .. } |
203 ProjectionElem::Field(..) |
204 ProjectionElem::ConstantIndex { .. } |
205 ProjectionElem::Downcast(..) => qualif,
207 // FIXME(eddyb) shouldn't this be masked *after* including the
208 // index local? Then again, it's `usize` which is neither
209 // `HasMutInterior` nor `NeedsDrop`.
210 ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
213 bug!("This should be called if projection is not empty");
218 cx: &ConstCx<'_, 'tcx>,
219 place: PlaceRef<'_, 'tcx>,
221 Self::in_projection_structurally(cx, place)
224 fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
227 base: PlaceBase::Local(local),
229 } => Self::in_local(cx, *local),
231 base: PlaceBase::Static(box Static {
232 kind: StaticKind::Promoted(..),
236 } => bug!("qualifying already promoted MIR"),
238 base: PlaceBase::Static(static_),
241 Self::in_static(cx, static_)
246 } => Self::in_projection(cx, place),
250 fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
252 Operand::Copy(ref place) |
253 Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
255 Operand::Constant(ref constant) => {
256 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
257 // Don't peek inside trait associated constants.
258 if cx.tcx.trait_of_item(def_id).is_some() {
259 Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
261 let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
263 let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
265 // Just in case the type is more specific than
266 // the definition, e.g., impl associated const
267 // with type parameters, take it into account.
268 qualif && Self::mask_for_ty(cx, constant.literal.ty)
277 fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
279 Rvalue::NullaryOp(..) => false,
281 Rvalue::Discriminant(ref place) |
282 Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
284 Rvalue::Use(ref operand) |
285 Rvalue::Repeat(ref operand, _) |
286 Rvalue::UnaryOp(_, ref operand) |
287 Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
289 Rvalue::BinaryOp(_, ref lhs, ref rhs) |
290 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
291 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
294 Rvalue::Ref(_, _, ref place) => {
295 // Special-case reborrows to be more like a copy of the reference.
296 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
297 if ProjectionElem::Deref == elem {
298 let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
299 if let ty::Ref(..) = base_ty.kind {
300 return Self::in_place(cx, PlaceRef {
302 projection: proj_base,
308 Self::in_place(cx, place.as_ref())
311 Rvalue::Aggregate(_, ref operands) => {
312 operands.iter().any(|o| Self::in_operand(cx, o))
317 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
318 Self::in_rvalue_structurally(cx, rvalue)
322 cx: &ConstCx<'_, 'tcx>,
323 _callee: &Operand<'tcx>,
324 _args: &[Operand<'tcx>],
327 // Be conservative about the returned value of a const fn.
328 Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
331 fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
333 ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
334 ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
335 ValueSource::Call { callee, args, return_ty } => {
336 Self::in_call(cx, callee, args, return_ty)
342 /// Constant containing interior mutability (`UnsafeCell<T>`).
343 /// This must be ruled out to make sure that evaluating the constant at compile-time
344 /// and at *any point* during the run-time would produce the same result. In particular,
345 /// promotion of temporaries must not change program behavior; if the promoted could be
346 /// written to, that would be a problem.
347 struct HasMutInterior;
349 impl Qualif for HasMutInterior {
350 const IDX: usize = 0;
352 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
353 Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
356 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
358 // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
359 // allowed in constants (and the `Checker` will error), and/or it
360 // won't be promoted, due to `&mut ...` or interior mutability.
361 Rvalue::Ref(_, kind, ref place) => {
362 let ty = place.ty(cx.body, cx.tcx).ty;
364 if let BorrowKind::Mut { .. } = kind {
365 // In theory, any zero-sized value could be borrowed
366 // mutably without consequences. However, only &mut []
367 // is allowed right now, and only in functions.
368 if cx.mode == Mode::StaticMut {
369 // Inside a `static mut`, &mut [...] is also allowed.
371 ty::Array(..) | ty::Slice(_) => {}
374 } else if let ty::Array(_, len) = ty.kind {
375 // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
376 // seems unnecessary, given that this is merely a ZST.
377 match len.try_eval_usize(cx.tcx, cx.param_env) {
378 Some(0) if cx.mode == Mode::NonConstFn => {},
387 Rvalue::Aggregate(ref kind, _) => {
388 if let AggregateKind::Adt(def, ..) = **kind {
389 if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
390 let ty = rvalue.ty(cx.body, cx.tcx);
391 assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
400 Self::in_rvalue_structurally(cx, rvalue)
404 /// Constant containing an ADT that implements `Drop`.
405 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
406 /// as that might not be a `const fn`, and (b) because implicit promotion would
407 /// remove side-effects that occur as part of dropping that value.
410 impl Qualif for NeedsDrop {
411 const IDX: usize = 1;
413 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
414 Some(ty.needs_drop(cx.tcx, cx.param_env))
417 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
418 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
419 if let AggregateKind::Adt(def, ..) = **kind {
420 if def.has_dtor(cx.tcx) {
426 Self::in_rvalue_structurally(cx, rvalue)
430 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
431 macro_rules! static_assert_seq_qualifs {
432 ($i:expr => $first:ident $(, $rest:ident)*) => {
434 static_assert_seq_qualifs!($i + 1 => $($rest),*);
440 static_assert!(QUALIF_COUNT == $i);
443 static_assert_seq_qualifs!(
444 0 => HasMutInterior, NeedsDrop
447 impl ConstCx<'_, 'tcx> {
448 fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
449 let mut qualifs = PerQualif::default();
450 qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
451 qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
455 fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
456 let mut qualifs = PerQualif::default();
457 qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
458 qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
462 fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
463 let mut qualifs = PerQualif::default();
464 qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
465 qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
470 /// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
471 /// for value qualifications, and accumulates writes of
472 /// rvalue/call results to locals, in `local_qualif`.
473 /// It also records candidates for promotion in `promotion_candidates`,
474 /// both in functions and const/static items.
475 struct Checker<'a, 'tcx> {
476 cx: ConstCx<'a, 'tcx>,
480 rpo: ReversePostorder<'a, 'tcx>,
482 temp_promotion_state: IndexVec<Local, TempState>,
483 unchecked_promotion_candidates: Vec<Candidate>,
485 /// If `true`, do not emit errors to the user, merely collect them in `errors`.
486 suppress_errors: bool,
487 errors: Vec<(Span, String)>,
490 macro_rules! unleash_miri {
492 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
493 if $this.mode.requires_const_checking() && !$this.suppress_errors {
494 $this.tcx.sess.span_warn($this.span, "skipping const checks");
501 impl Deref for Checker<'a, 'tcx> {
502 type Target = ConstCx<'a, 'tcx>;
504 fn deref(&self) -> &Self::Target {
509 impl<'a, 'tcx> Checker<'a, 'tcx> {
510 fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
511 assert!(def_id.is_local());
512 let mut rpo = traversal::reverse_postorder(body);
513 let (temps, unchecked_promotion_candidates) =
514 promote_consts::collect_temps_and_candidates(tcx, body, &mut rpo);
517 let param_env = tcx.param_env(def_id);
519 let mut cx = ConstCx {
524 per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
527 for (local, decl) in body.local_decls.iter_enumerated() {
528 if let LocalKind::Arg = body.local_kind(local) {
529 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
530 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
532 per_local.insert(local);
543 temp_promotion_state: temps,
544 unchecked_promotion_candidates,
546 suppress_errors: false,
550 // FIXME(eddyb) we could split the errors into meaningful
551 // categories, but enabling full miri would make that
552 // slightly pointless (even with feature-gating).
553 fn not_const(&mut self, op: impl NonConstOp) {
555 if self.mode.requires_const_checking() && !self.suppress_errors {
556 self.record_error(op);
557 let mut err = struct_span_err!(
561 "{} contains unimplemented expression type",
564 if self.tcx.sess.teach(&err.get_code().unwrap()) {
565 err.note("A function call isn't allowed in the const's initialization expression \
566 because the expression's value must be known at compile-time.");
567 err.note("Remember: you can't use a function call inside a const's initialization \
568 expression! However, you can use it anywhere else.");
574 fn record_error(&mut self, op: impl NonConstOp) {
575 self.record_error_spanned(op, self.span);
578 fn record_error_spanned(&mut self, op: impl NonConstOp, span: Span) {
579 self.errors.push((span, format!("{:?}", op)));
582 /// Assigns an rvalue/call qualification to the given destination.
583 fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
584 trace!("assign: {:?} <- {:?}", dest, source);
586 let mut qualifs = self.qualifs_in_value(source);
589 ValueSource::Rvalue(&Rvalue::Ref(_, kind, _)) => {
590 // Getting `true` from `HasMutInterior::in_rvalue` means
591 // the borrowed place is disallowed from being borrowed,
592 // due to either a mutable borrow (with some exceptions),
593 // or an shared borrow of a value with interior mutability.
594 // Then `HasMutInterior` is cleared
595 // to avoid duplicate errors (e.g. from reborrowing).
596 if qualifs[HasMutInterior] {
597 qualifs[HasMutInterior] = false;
599 debug!("suppress_errors: {}", self.suppress_errors);
600 if self.mode.requires_const_checking() && !self.suppress_errors {
601 if !self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
602 self.record_error(ops::MutBorrow(kind));
603 if let BorrowKind::Mut { .. } = kind {
604 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
605 "references in {}s may only refer \
606 to immutable values", self.mode);
607 err.span_label(self.span, format!("{}s require immutable values",
609 if self.tcx.sess.teach(&err.get_code().unwrap()) {
610 err.note("References in statics and constants may only refer \
611 to immutable values.\n\n\
612 Statics are shared everywhere, and if they refer to \
613 mutable data one might violate memory safety since \
614 holding multiple mutable references to shared data \
616 If you really want global mutable state, try using \
617 static mut or a global UnsafeCell.");
621 span_err!(self.tcx.sess, self.span, E0492,
622 "cannot borrow a constant which may contain \
623 interior mutability, create a static instead");
632 let mut dest_projection = &dest.projection[..];
634 match (&dest.base, dest_projection) {
635 // We treat all locals equal in constants
636 (&PlaceBase::Local(index), []) => break index,
637 // projections are transparent for assignments
638 // we qualify the entire destination at once, even if just a field would have
639 // stricter qualification
640 (base, [proj_base @ .., _]) => {
641 // Catch more errors in the destination. `visit_place` also checks various
642 // projection rules like union field access and raw pointer deref
643 let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
644 self.visit_place_base(base, context, location);
645 self.visit_projection(base, dest_projection, context, location);
646 dest_projection = proj_base;
648 (&PlaceBase::Static(box Static {
649 kind: StaticKind::Promoted(..),
651 }), []) => bug!("promoteds don't exist yet during promotion"),
652 (&PlaceBase::Static(box Static{ kind: _, .. }), []) => {
653 // Catch more errors in the destination. `visit_place` also checks that we
654 // do not try to access statics from constants or try to mutate statics
655 let context = PlaceContext::MutatingUse(MutatingUseContext::Store);
656 self.visit_place_base(&dest.base, context, location);
662 let kind = self.body.local_kind(index);
663 debug!("store to {:?} {:?}", kind, index);
665 // Only handle promotable temps in non-const functions.
666 if self.mode == Mode::NonConstFn {
667 if kind != LocalKind::Temp ||
668 !self.temp_promotion_state[index].is_promotable() {
673 // this is overly restrictive, because even full assignments do not clear the qualif
674 // While we could special case full assignments, this would be inconsistent with
675 // aggregates where we overwrite all fields via assignments, which would not get
677 for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
679 per_local.insert(index);
684 /// Check a whole const, static initializer or const fn.
685 fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
686 use crate::transform::check_consts as new_checker;
688 debug!("const-checking {} {:?}", self.mode, self.def_id);
690 // FIXME: Also use the new validator when features that require it (e.g. `const_if`) are
692 let use_new_validator = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
693 if use_new_validator {
694 debug!("Using dataflow-based const validator");
697 let item = new_checker::Item::new(self.tcx, self.def_id, self.body);
698 let mut validator = new_checker::validation::Validator::new(&item);
700 validator.suppress_errors = !use_new_validator;
701 self.suppress_errors = use_new_validator;
703 let body = self.body;
705 let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
706 let mut bb = START_BLOCK;
707 let mut has_controlflow_error = false;
709 seen_blocks.insert(bb.index());
711 self.visit_basic_block_data(bb, &body[bb]);
712 validator.visit_basic_block_data(bb, &body[bb]);
714 let target = match body[bb].terminator().kind {
715 TerminatorKind::Goto { target } |
716 TerminatorKind::FalseUnwind { real_target: target, .. } |
717 TerminatorKind::Drop { target, .. } |
718 TerminatorKind::DropAndReplace { target, .. } |
719 TerminatorKind::Assert { target, .. } |
720 TerminatorKind::Call { destination: Some((_, target)), .. } => {
724 // Non-terminating calls cannot produce any value.
725 TerminatorKind::Call { destination: None, .. } => {
729 TerminatorKind::SwitchInt {..} |
730 TerminatorKind::Resume |
731 TerminatorKind::Abort |
732 TerminatorKind::GeneratorDrop |
733 TerminatorKind::Yield { .. } |
734 TerminatorKind::Unreachable |
735 TerminatorKind::FalseEdges { .. } => None,
737 TerminatorKind::Return => {
744 Some(target) if !seen_blocks.contains(target.index()) => {
748 has_controlflow_error = true;
749 self.not_const(ops::Loop);
750 validator.check_op(ops::Loop);
756 // The new validation pass should agree with the old when running on simple const bodies
757 // (e.g. no `if` or `loop`).
758 if !use_new_validator {
759 let mut new_errors = validator.take_errors();
761 // FIXME: each checker sometimes emits the same error with the same span twice in a row.
765 if self.errors != new_errors {
769 std::mem::replace(&mut self.errors, vec![]),
775 // Collect all the temps we need to promote.
776 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
778 // HACK: if parts of the control-flow graph were skipped due to an error, don't try to
779 // promote anything, since that can cause errors in a `const` if e.g. rvalue static
780 // promotion is attempted within a loop body.
781 let unleash_miri = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
782 let promotion_candidates = if has_controlflow_error && !unleash_miri {
783 self.tcx.sess.delay_span_bug(
785 "check_const: expected control-flow error(s)",
790 promote_consts::validate_candidates(
794 &self.temp_promotion_state,
795 &self.unchecked_promotion_candidates,
799 debug!("qualify_const: promotion_candidates={:?}", promotion_candidates);
800 for candidate in promotion_candidates {
802 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
803 if let StatementKind::Assign(box( _, Rvalue::Ref(_, _, place)))
804 = &self.body[bb].statements[stmt_idx].kind
806 if let PlaceBase::Local(local) = place.base {
807 promoted_temps.insert(local);
812 // Only rvalue-static promotion requires extending the lifetime of the promoted
814 Candidate::Argument { .. } | Candidate::Repeat(_) => {}
818 let qualifs = self.qualifs_in_local(RETURN_PLACE);
819 (qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
823 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
826 place_base: &PlaceBase<'tcx>,
827 context: PlaceContext,
830 self.super_place_base(place_base, context, location);
832 PlaceBase::Local(_) => {}
833 PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_, _), .. }) => {
836 PlaceBase::Static(box Static{ kind: StaticKind::Static, def_id, .. }) => {
840 .any(|attr| attr.check_name(sym::thread_local)) {
841 if self.mode.requires_const_checking() && !self.suppress_errors {
842 self.record_error(ops::ThreadLocalAccess);
843 span_err!(self.tcx.sess, self.span, E0625,
844 "thread-local statics cannot be \
845 accessed at compile-time");
850 // Only allow statics (not consts) to refer to other statics.
851 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
852 if self.mode == Mode::Static
853 && context.is_mutating_use()
854 && !self.suppress_errors
856 // this is not strictly necessary as miri will also bail out
857 // For interior mutability we can't really catch this statically as that
858 // goes through raw pointers and intermediate temporaries, so miri has
859 // to catch this anyway
860 self.tcx.sess.span_err(
862 "cannot mutate statics in the initializer of another static",
869 if self.mode.requires_const_checking() && !self.suppress_errors {
870 self.record_error(ops::StaticAccess);
871 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
872 "{}s cannot refer to statics, use \
873 a constant instead", self.mode);
874 if self.tcx.sess.teach(&err.get_code().unwrap()) {
876 "Static and const variables can refer to other const variables. \
877 But a const variable cannot refer to a static variable."
880 "To fix this, the value can be extracted as a const and then used."
889 fn visit_projection_elem(
891 place_base: &PlaceBase<'tcx>,
892 proj_base: &[PlaceElem<'tcx>],
893 elem: &PlaceElem<'tcx>,
894 context: PlaceContext,
898 "visit_projection_elem: place_base={:?} proj_base={:?} elem={:?} \
899 context={:?} location={:?}",
907 self.super_projection_elem(place_base, proj_base, elem, context, location);
910 ProjectionElem::Deref => {
911 if context.is_mutating_use() {
912 // `not_const` errors out in const contexts
913 self.not_const(ops::MutDeref)
915 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
917 Mode::NonConstFn => {}
918 _ if self.suppress_errors => {}
920 if let ty::RawPtr(_) = base_ty.kind {
921 if !self.tcx.features().const_raw_ptr_deref {
922 self.record_error(ops::RawPtrDeref);
924 &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
925 self.span, GateIssue::Language,
927 "dereferencing raw pointers in {}s is unstable",
937 ProjectionElem::ConstantIndex {..} |
938 ProjectionElem::Subslice {..} |
939 ProjectionElem::Field(..) |
940 ProjectionElem::Index(_) => {
941 let base_ty = Place::ty_from(place_base, proj_base, self.body, self.tcx).ty;
942 if let Some(def) = base_ty.ty_adt_def() {
946 if !self.tcx.features().const_fn_union
947 && !self.suppress_errors
949 self.record_error(ops::UnionAccess);
951 &self.tcx.sess.parse_sess, sym::const_fn_union,
952 self.span, GateIssue::Language,
953 "unions in const fn are unstable",
968 ProjectionElem::Downcast(..) => {
969 self.not_const(ops::Downcast)
974 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
975 debug!("visit_operand: operand={:?} location={:?}", operand, location);
976 self.super_operand(operand, location);
979 Operand::Move(ref place) => {
980 // Mark the consumed locals to indicate later drops are noops.
981 if let Some(local) = place.as_local() {
982 self.cx.per_local[NeedsDrop].remove(local);
986 Operand::Constant(_) => {}
990 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
991 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
993 // Check nested operands and places.
994 if let Rvalue::Ref(_, kind, ref place) = *rvalue {
995 // Special-case reborrows.
996 let mut reborrow_place = None;
997 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
998 if elem == ProjectionElem::Deref {
999 let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
1000 if let ty::Ref(..) = base_ty.kind {
1001 reborrow_place = Some(proj_base);
1006 if let Some(proj) = reborrow_place {
1007 let ctx = match kind {
1008 BorrowKind::Shared => PlaceContext::NonMutatingUse(
1009 NonMutatingUseContext::SharedBorrow,
1011 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
1012 NonMutatingUseContext::ShallowBorrow,
1014 BorrowKind::Unique => PlaceContext::NonMutatingUse(
1015 NonMutatingUseContext::UniqueBorrow,
1017 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
1018 MutatingUseContext::Borrow,
1021 self.visit_place_base(&place.base, ctx, location);
1022 self.visit_projection(&place.base, proj, ctx, location);
1024 self.super_rvalue(rvalue, location);
1027 self.super_rvalue(rvalue, location);
1032 Rvalue::Repeat(..) |
1033 Rvalue::UnaryOp(UnOp::Neg, _) |
1034 Rvalue::UnaryOp(UnOp::Not, _) |
1035 Rvalue::NullaryOp(NullOp::SizeOf, _) |
1036 Rvalue::CheckedBinaryOp(..) |
1037 Rvalue::Cast(CastKind::Pointer(_), ..) |
1038 Rvalue::Discriminant(..) |
1041 Rvalue::Aggregate(..) => {}
1043 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
1044 let operand_ty = operand.ty(self.body, self.tcx);
1045 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
1046 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
1047 match (cast_in, cast_out) {
1048 (CastTy::Ptr(_), CastTy::Int(_)) |
1049 (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
1050 unleash_miri!(self);
1051 if !self.tcx.features().const_raw_ptr_to_usize_cast
1052 && !self.suppress_errors
1054 // in const fn and constants require the feature gate
1055 // FIXME: make it unsafe inside const fn and constants
1056 self.record_error(ops::RawPtrToIntCast);
1058 &self.tcx.sess.parse_sess, sym::const_raw_ptr_to_usize_cast,
1059 self.span, GateIssue::Language,
1061 "casting pointers to integers in {}s is unstable",
1071 Rvalue::BinaryOp(op, ref lhs, _) => {
1072 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
1073 assert!(op == BinOp::Eq || op == BinOp::Ne ||
1074 op == BinOp::Le || op == BinOp::Lt ||
1075 op == BinOp::Ge || op == BinOp::Gt ||
1076 op == BinOp::Offset);
1078 unleash_miri!(self);
1079 if self.mode.requires_const_checking() &&
1080 !self.tcx.features().const_compare_raw_pointers &&
1081 !self.suppress_errors
1083 self.record_error(ops::RawPtrComparison);
1084 // require the feature gate inside constants and const fn
1085 // FIXME: make it unsafe to use these operations
1087 &self.tcx.sess.parse_sess,
1088 sym::const_compare_raw_pointers,
1090 GateIssue::Language,
1091 &format!("comparing raw pointers inside {}", self.mode),
1097 Rvalue::NullaryOp(NullOp::Box, _) => {
1098 unleash_miri!(self);
1099 if self.mode.requires_const_checking() && !self.suppress_errors {
1100 self.record_error(ops::HeapAllocation);
1101 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1102 "allocations are not allowed in {}s", self.mode);
1103 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1104 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1106 "The value of statics and constants must be known at compile time, \
1107 and they live for the entire lifetime of a program. Creating a boxed \
1108 value allocates memory on the heap at runtime, and therefore cannot \
1109 be done at compile time."
1118 fn visit_terminator_kind(&mut self,
1119 kind: &TerminatorKind<'tcx>,
1120 location: Location) {
1121 debug!("visit_terminator_kind: kind={:?} location={:?}", kind, location);
1122 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1123 if let Some((ref dest, _)) = *destination {
1124 self.assign(dest, ValueSource::Call {
1127 return_ty: dest.ty(self.body, self.tcx).ty,
1131 let fn_ty = func.ty(self.body, self.tcx);
1133 ty::FnDef(def_id, _) => {
1134 match self.tcx.fn_sig(def_id).abi() {
1135 Abi::RustIntrinsic |
1136 Abi::PlatformIntrinsic => {
1137 assert!(!self.tcx.is_const_fn(def_id));
1138 match &*self.tcx.item_name(def_id).as_str() {
1139 // special intrinsic that can be called diretly without an intrinsic
1140 // feature gate needs a language feature gate
1142 if self.mode.requires_const_checking()
1143 && !self.suppress_errors
1145 // const eval transmute calls only with the feature gate
1146 if !self.tcx.features().const_transmute {
1147 self.record_error(ops::Transmute);
1149 &self.tcx.sess.parse_sess, sym::const_transmute,
1150 self.span, GateIssue::Language,
1151 &format!("The use of std::mem::transmute() \
1152 is gated in {}s", self.mode));
1157 // no need to check feature gates, intrinsics are only callable
1158 // from the libstd or with forever unstable feature gates
1163 // In normal functions no calls are feature-gated.
1164 if self.mode.requires_const_checking() {
1165 let unleash_miri = self
1170 .unleash_the_miri_inside_of_you;
1171 if self.tcx.is_const_fn(def_id)
1173 || self.suppress_errors
1175 // stable const fns or unstable const fns
1176 // with their feature gate active
1177 // FIXME(eddyb) move stability checks from `is_const_fn` here.
1178 } else if self.is_const_panic_fn(def_id) {
1179 // Check the const_panic feature gate.
1180 // FIXME: cannot allow this inside `allow_internal_unstable`
1181 // because that would make `panic!` insta stable in constants,
1182 // since the macro is marked with the attribute.
1183 if !self.tcx.features().const_panic {
1184 // Don't allow panics in constants without the feature gate.
1185 self.record_error(ops::Panic);
1187 &self.tcx.sess.parse_sess,
1190 GateIssue::Language,
1191 &format!("panicking in {}s is unstable", self.mode),
1194 } else if let Some(feature)
1195 = self.tcx.is_unstable_const_fn(def_id) {
1196 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1197 // functions without the feature gate active in this crate in
1198 // order to report a better error message than the one below.
1199 if !self.span.allows_unstable(feature) {
1200 self.record_error(ops::FnCallUnstable(def_id, feature));
1201 let mut err = self.tcx.sess.struct_span_err(self.span,
1202 &format!("`{}` is not yet stable as a const fn",
1203 self.tcx.def_path_str(def_id)));
1204 if nightly_options::is_nightly_build() {
1206 "add `#![feature({})]` to the \
1207 crate attributes to enable",
1213 self.record_error(ops::FnCallNonConst(def_id));
1214 let mut err = struct_span_err!(
1218 "calls in {}s are limited to constant functions, \
1219 tuple structs and tuple variants",
1229 unleash_miri!(self);
1230 if self.mode.requires_const_checking() && !self.suppress_errors {
1231 self.record_error(ops::FnCallIndirect);
1232 let mut err = self.tcx.sess.struct_span_err(
1234 "function pointers are not allowed in const fn"
1240 self.not_const(ops::FnCallOther);
1244 // Check callee and argument operands.
1245 self.visit_operand(func, location);
1247 self.visit_operand(arg, location);
1249 } else if let TerminatorKind::Drop {
1250 location: ref place, ..
1251 } | TerminatorKind::DropAndReplace {
1252 location: ref place, ..
1255 TerminatorKind::DropAndReplace { .. } => {}
1256 _ => self.super_terminator_kind(kind, location),
1259 // Deny *any* live drops anywhere other than functions.
1260 if self.mode.requires_const_checking() && !self.suppress_errors {
1261 unleash_miri!(self);
1262 // HACK(eddyb): emulate a bit of dataflow analysis,
1263 // conservatively, that drop elaboration will do.
1264 let needs_drop = if let Some(local) = place.as_local() {
1265 if NeedsDrop::in_local(self, local) {
1266 Some(self.body.local_decls[local].source_info.span)
1274 if let Some(span) = needs_drop {
1275 // Double-check the type being dropped, to minimize false positives.
1276 let ty = place.ty(self.body, self.tcx).ty;
1277 if ty.needs_drop(self.tcx, self.param_env) {
1278 self.record_error_spanned(ops::LiveDrop, span);
1279 struct_span_err!(self.tcx.sess, span, E0493,
1280 "destructors cannot be evaluated at compile-time")
1281 .span_label(span, format!("{}s cannot evaluate destructors",
1289 TerminatorKind::DropAndReplace { ref value, .. } => {
1290 self.assign(place, ValueSource::DropAndReplace(value), location);
1291 self.visit_operand(value, location);
1296 // Qualify any operands inside other terminators.
1297 self.super_terminator_kind(kind, location);
1301 fn visit_assign(&mut self,
1303 rvalue: &Rvalue<'tcx>,
1304 location: Location) {
1305 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1306 self.assign(dest, ValueSource::Rvalue(rvalue), location);
1308 self.visit_rvalue(rvalue, location);
1311 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1312 debug!("visit_source_info: source_info={:?}", source_info);
1313 self.span = source_info.span;
1316 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1317 debug!("visit_statement: statement={:?} location={:?}", statement, location);
1318 match statement.kind {
1319 StatementKind::Assign(..) => {
1320 self.super_statement(statement, location);
1322 StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _) => {
1323 self.not_const(ops::IfOrMatch);
1325 // FIXME(eddyb) should these really do nothing?
1326 StatementKind::FakeRead(..) |
1327 StatementKind::SetDiscriminant { .. } |
1328 StatementKind::StorageLive(_) |
1329 StatementKind::StorageDead(_) |
1330 StatementKind::InlineAsm {..} |
1331 StatementKind::Retag { .. } |
1332 StatementKind::AscribeUserType(..) |
1333 StatementKind::Nop => {}
1338 pub fn provide(providers: &mut Providers<'_>) {
1339 *providers = Providers {
1345 // FIXME(eddyb) this is only left around for the validation logic
1346 // in `promote_consts`, see the comment in `validate_operand`.
1347 pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
1349 fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
1350 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1351 // cannot yet be stolen), because `mir_validated()`, which steals
1352 // from `mir_const(), forces this query to execute before
1353 // performing the steal.
1354 let body = &tcx.mir_const(def_id).borrow();
1356 if body.return_ty().references_error() {
1357 tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
1358 return (QUALIF_ERROR_BIT, tcx.arena.alloc(BitSet::new_empty(0)));
1361 Checker::new(tcx, def_id, body, Mode::Const).check_const()
1364 pub struct QualifyAndPromoteConstants<'tcx> {
1365 pub promoted: Cell<IndexVec<Promoted, Body<'tcx>>>,
1368 impl<'tcx> Default for QualifyAndPromoteConstants<'tcx> {
1369 fn default() -> Self {
1370 QualifyAndPromoteConstants {
1371 promoted: Cell::new(IndexVec::new()),
1376 impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
1377 fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
1378 // There's not really any point in promoting errorful MIR.
1379 if body.return_ty().references_error() {
1380 tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
1384 if src.promoted.is_some() {
1388 let def_id = src.def_id();
1389 let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1391 let mode = determine_mode(tcx, hir_id, def_id);
1393 debug!("run_pass: mode={:?}", mode);
1394 if let Mode::NonConstFn | Mode::ConstFn = mode {
1395 let mut checker = Checker::new(tcx, def_id, body, mode);
1396 if let Mode::ConstFn = mode {
1397 let use_min_const_fn_checks =
1398 !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
1399 tcx.is_min_const_fn(def_id);
1400 if use_min_const_fn_checks {
1401 // Enforce `min_const_fn` for stable `const fn`s.
1402 use super::qualify_min_const_fn::is_min_const_fn;
1403 if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1404 error_min_const_fn_violation(tcx, span, err);
1408 // `check_const` should not produce any errors, but better safe than sorry
1410 // NOTE(eddyb) `check_const` is actually needed for promotion inside
1411 // `min_const_fn` functions.
1414 // Enforce a constant-like CFG for `const fn`.
1415 checker.check_const();
1417 while let Some((bb, data)) = checker.rpo.next() {
1418 checker.visit_basic_block_data(bb, data);
1422 // Promote only the promotable candidates.
1423 let temps = checker.temp_promotion_state;
1424 let candidates = promote_consts::validate_candidates(
1429 &checker.unchecked_promotion_candidates,
1432 // Do the actual promotion, now that we know what's viable.
1434 promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
1437 check_short_circuiting_in_const_local(tcx, body, mode);
1439 let promoted_temps = match mode {
1440 Mode::Const => tcx.mir_const_qualif(def_id).1,
1441 _ => Checker::new(tcx, def_id, body, mode).check_const().1,
1443 remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
1446 if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
1447 // `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
1448 check_static_is_sync(tcx, body, hir_id);
1453 fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
1454 match tcx.hir().body_owner_kind(hir_id) {
1455 hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1456 hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
1457 hir::BodyOwnerKind::Fn => Mode::NonConstFn,
1458 hir::BodyOwnerKind::Const => Mode::Const,
1459 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1460 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1464 fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
1465 struct_span_err!(tcx.sess, span, E0723, "{}", msg)
1466 .note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
1467 .help("add `#![feature(const_fn)]` to the crate attributes to enable")
1471 fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
1472 if body.control_flow_destroyed.is_empty() {
1476 let mut locals = body.vars_iter();
1477 if let Some(local) = locals.next() {
1478 let span = body.local_decls[local].source_info.span;
1479 let mut error = tcx.sess.struct_span_err(
1482 "new features like let bindings are not permitted in {}s \
1483 which also use short circuiting operators",
1487 for (span, kind) in body.control_flow_destroyed.iter() {
1490 &format!("use of {} here does not actually short circuit due to \
1491 the const evaluator presently not being able to do control flow. \
1492 See https://github.com/rust-lang/rust/issues/49146 for more \
1493 information.", kind),
1496 for local in locals {
1497 let span = body.local_decls[local].source_info.span;
1498 error.span_note(span, "more locals defined here");
1504 /// In `const` and `static` everything without `StorageDead`
1505 /// is `'static`, we don't have to create promoted MIR fragments,
1506 /// just remove `Drop` and `StorageDead` on "promoted" locals.
1507 fn remove_drop_and_storage_dead_on_promoted_locals(
1508 body: &mut Body<'tcx>,
1509 promoted_temps: &BitSet<Local>,
1511 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1513 for block in body.basic_blocks_mut() {
1514 block.statements.retain(|statement| {
1515 match statement.kind {
1516 StatementKind::StorageDead(index) => !promoted_temps.contains(index),
1520 let terminator = block.terminator_mut();
1521 match &terminator.kind {
1522 TerminatorKind::Drop {
1527 if let Some(index) = location.as_local() {
1528 if promoted_temps.contains(index) {
1529 terminator.kind = TerminatorKind::Goto { target: *target };
1538 fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
1539 let ty = body.return_ty();
1540 tcx.infer_ctxt().enter(|infcx| {
1541 let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1542 let mut fulfillment_cx = traits::FulfillmentContext::new();
1543 let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
1544 fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1545 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1546 infcx.report_fulfillment_errors(&err, None, false);
1551 fn validator_mismatch(
1554 mut old_errors: Vec<(Span, String)>,
1555 mut new_errors: Vec<(Span, String)>,
1557 error!("old validator: {:?}", old_errors);
1558 error!("new validator: {:?}", new_errors);
1560 // ICE on nightly if the validators do not emit exactly the same errors.
1561 // Users can supress this panic with an unstable compiler flag (hopefully after
1562 // filing an issue).
1563 let opts = &tcx.sess.opts;
1564 let strict_validation_enabled = opts.unstable_features.is_nightly_build()
1565 && !opts.debugging_opts.suppress_const_validation_back_compat_ice;
1567 if !strict_validation_enabled {
1571 // If this difference would cause a regression from the old to the new or vice versa, trigger
1573 if old_errors.is_empty() || new_errors.is_empty() {
1574 span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1577 // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the
1578 // new validator to be more conservative about when a dropped local has been moved out of.
1580 // Supress the mismatch ICE in cases where the validators disagree only on the number of
1581 // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s.
1583 let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop");
1584 let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow");
1586 let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect();
1587 let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect();
1589 let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors;
1591 let old_mut_borrows = old_errors.iter().filter(is_mut_borrow);
1592 let new_mut_borrows = new_errors.iter().filter(is_mut_borrow);
1594 let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some();
1596 if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) {
1600 span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR);
1603 const VALIDATOR_MISMATCH_ERR: &str =
1604 r"Disagreement between legacy and dataflow-based const validators.
1605 After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code.";