diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index a837c34e8d474..98d5487870a4d 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -14,6 +14,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(core_intrinsics)] #![feature(const_fn)] #![feature(decl_macro)] +#![feature(drain_filter)] #![feature(exhaustive_patterns)] #![feature(never_type)] #![feature(specialization)] diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index fbcf9c8cb5eba..da1abb9747c1a 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1024,23 +1024,12 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { new_errors.dedup(); if self.errors != new_errors { - 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, - ); - } + validator_mismatch( + self.tcx, + body, + std::mem::replace(&mut self.errors, vec![]), + new_errors, + ); } } @@ -1870,6 +1859,58 @@ fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option, + 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); +} + 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."; diff --git a/src/test/ui/consts/const-eval/issue-65394.rs b/src/test/ui/consts/const-eval/issue-65394.rs new file mode 100644 index 0000000000000..978e227bcc817 --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-65394.rs @@ -0,0 +1,13 @@ +// Test for absence of validation mismatch ICE in #65394 + +#![feature(rustc_attrs)] + +#[rustc_mir(borrowck_graphviz_postflow="hello.dot")] +const _: Vec = { + let mut x = Vec::::new(); + let r = &mut x; //~ ERROR references in constants may only refer to immutable values + let y = x; + y +}; + +fn main() {} diff --git a/src/test/ui/consts/const-eval/issue-65394.stderr b/src/test/ui/consts/const-eval/issue-65394.stderr new file mode 100644 index 0000000000000..f48c551cb50f5 --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-65394.stderr @@ -0,0 +1,11 @@ +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`.