Change:
Offending snippet using : The code shouldn't build, as per the first few errors below, but currently on nightly it also causes the compiler to panic. It does not panic on stable or beta. Version: 1.40.0-nightly (2019-10-12 ) Playground: Quite possibly a duplicate of just buried inside the macro expansion but I'm not familiar enough with Rust compiler internals to make that determination.
cc
I've to the following: A mutable reference is required in all variants of this, so this particular mismatch does not represent a true backwards compatibility hazard: Creating a reference that would allow mutation is forbidden in all contexts. We could simply stop triggering the mismatch ICE when both lists contain the same error. The underlying issue, however, is that is being too conservative once again. As soon as a mutable reference is taken to , we use type-based qualification instead of looking at the qualif which gets cleared when is moved from. Increasing the precision here would require a dataflow analysis. This would get checked first whenever was queried (much like how we currently check first). I believe this will be necessary when we do allow in contexts, otherwise writing code would be quite painful. I could hack together a solution for this particular case that doesn't require another dataflow analysis. Basically, during validation, we could keep track of which s have been moved out of in the current basic block and use that as a conservative approximation of . This would fail if the local was moved from in one basic block and then dropped in a successor of that block. It seems relatively hard to write rust code that gets translated to that MIR, however. For example, the following does not do it, but I think it should be possible.
cc
Checking for moves in the same block fails pretty often. I've implemented the first fix described above, but we probably want to use a analysis in the long term.
triage: P-medium. Assigning to . Removing nomination label.

Discussion:
#![feature(core_intrinsics)] #![feature(const_fn)] #![feature(decl_macro)] <ins> #![feature(drain_filter)] </ins> #![feature(exhaustive_patterns)] #![feature(never_type)] #![feature(specialization)]

new_errors.dedup(); if self.errors != new_errors { <del> error!("old validator: {:?}", self.errors); error!("new validator: {:?}", new_errors); // ICE on nightly if the validators do not emit exactly the same errors. // Users can supress this panic with an unstable compiler flag (hopefully after // filing an issue). let opts = &self.tcx.sess.opts; let trigger_ice = opts.unstable_features.is_nightly_build() && !opts.debugging_opts.suppress_const_validation_back_compat_ice; if trigger_ice { span_bug!( body.span, "{}", VALIDATOR_MISMATCH_ERR, ); } </del> <ins> validator_mismatch( self.tcx, body, std::mem::replace(&mut self.errors, vec![]), new_errors, ); </ins> } }

Some(ret) } <ins> fn validator_mismatch( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, mut old_errors: Vec<(Span, String)>, mut new_errors: Vec<(Span, String)>, ) { error!("old validator: {:?}", old_errors); error!("new validator: {:?}", new_errors); // ICE on nightly if the validators do not emit exactly the same errors. // Users can supress this panic with an unstable compiler flag (hopefully after // filing an issue). let opts = &tcx.sess.opts; let strict_validation_enabled = opts.unstable_features.is_nightly_build() && !opts.debugging_opts.suppress_const_validation_back_compat_ice; if !strict_validation_enabled { return; } // If this difference would cause a regression from the old to the new or vice versa, trigger // the ICE. if old_errors.is_empty() || new_errors.is_empty() { span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR); } // HACK: Borrows that would allow mutation are forbidden in const contexts, but they cause the // new validator to be more conservative about when a dropped local has been moved out of. // // Supress the mismatch ICE in cases where the validators disagree only on the number of // `LiveDrop` errors and both observe the same sequence of `MutBorrow`s. let is_live_drop = |(_, s): &mut (_, String)| s.starts_with("LiveDrop"); let is_mut_borrow = |(_, s): &&(_, String)| s.starts_with("MutBorrow"); let old_live_drops: Vec<_> = old_errors.drain_filter(is_live_drop).collect(); let new_live_drops: Vec<_> = new_errors.drain_filter(is_live_drop).collect(); let only_live_drops_differ = old_live_drops != new_live_drops && old_errors == new_errors; let old_mut_borrows = old_errors.iter().filter(is_mut_borrow); let new_mut_borrows = new_errors.iter().filter(is_mut_borrow); let at_least_one_mut_borrow = old_mut_borrows.clone().next().is_some(); if only_live_drops_differ && at_least_one_mut_borrow && old_mut_borrows.eq(new_mut_borrows) { return; } span_bug!(body.span, "{}", VALIDATOR_MISMATCH_ERR); } </ins> const VALIDATOR_MISMATCH_ERR: &str = r"Disagreement between legacy and dataflow-based const validators. After filing an issue, use `-Zsuppress-const-validation-back-compat-ice` to compile your code.";

<ins> // Test for absence of validation mismatch ICE in #65394 #![feature(rustc_attrs)] #[rustc_mir(borrowck_graphviz_postflow="hello.dot")] const _: Vec<i32> = { let mut x = Vec::<i32>::new(); let r = &mut x; //~ ERROR references in constants may only refer to immutable values let y = x; y }; fn main() {} </ins>

<ins> error[E0017]: references in constants may only refer to immutable values --> $DIR/issue-65394.rs:8:13 | LL |     let r = &mut x; |             ^^^^^^ constants require immutable values [ERROR rustc_mir::transform::qualify_consts] old validator: [($DIR/issue-65394.rs:8:13: 8:19, "MutBorrow(Mut { allow_two_phase_borrow: false })")] [ERROR rustc_mir::transform::qualify_consts] new validator: [($DIR/issue-65394.rs:8:13: 8:19, "MutBorrow(Mut { allow_two_phase_borrow: false })"), ($DIR/issue-65394.rs:7:9: 7:14, "LiveDrop")] error: aborting due to previous error For more information about this error, try `rustc --explain E0017`. </ins>