diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4b1aefb2216fb..577246783a178 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1300,6 +1300,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print some statistics about AST and HIR"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata"), + unleash_the_miri_inside_of_you: bool = (false, parse_bool, [TRACKED], + "take the breaks off const evaluation. NOTE: this is unsound"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], "pass `-install_name @rpath/...` to the macOS linker"), sanitizer: Option = (None, parse_sanitizer, [TRACKED], diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2d941902debc3..7d1943e21b90d 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -108,6 +108,15 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { promotion_candidates: Vec } +macro_rules! unleash_miri { + ($this:expr) => {{ + if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { + $this.tcx.sess.span_warn($this.span, "skipping const checks"); + return; + } + }} +} + impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, @@ -147,6 +156,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { // categories, but enabling full miri would make that // slightly pointless (even with feature-gating). fn not_const(&mut self) { + unleash_miri!(self); self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { let mut err = struct_span_err!( @@ -419,6 +429,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } return; } + unleash_miri!(self); self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { @@ -618,6 +629,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if forbidden_mut { + unleash_miri!(self); self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0017, @@ -660,6 +672,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut); if forbidden_mut { + unleash_miri!(self); self.add(Qualif::NOT_CONST); } else { // We might have a candidate for promotion. @@ -700,6 +713,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { match (cast_in, cast_out) { (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { + unleash_miri!(self); if let Mode::Fn = self.mode { // in normal functions, mark such casts as not promotable self.add(Qualif::NOT_CONST); @@ -727,6 +741,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { op == BinOp::Ge || op == BinOp::Gt || op == BinOp::Offset); + unleash_miri!(self); if let Mode::Fn = self.mode { // raw pointer operations are not allowed inside promoteds self.add(Qualif::NOT_CONST); @@ -745,6 +760,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } Rvalue::NullaryOp(NullOp::Box, _) => { + unleash_miri!(self); self.add(Qualif::NOT_CONST); if self.mode != Mode::Fn { let mut err = struct_span_err!(self.tcx.sess, self.span, E0010, @@ -861,7 +877,13 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } else { // stable const fns or unstable const fns with their feature gate // active - if self.tcx.is_const_fn(def_id) { + let unleash_miri = self + .tcx + .sess + .opts + .debugging_opts + .unleash_the_miri_inside_of_you; + if self.tcx.is_const_fn(def_id) || unleash_miri { is_const_fn = true; } else if self.is_const_panic_fn(def_id) { // Check the const_panic feature gate. @@ -1030,6 +1052,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { // Deny *any* live drops anywhere other than functions. if self.mode != Mode::Fn { + unleash_miri!(self); // HACK(eddyb): emulate a bit of dataflow analysis, // conservatively, that drop elaboration will do. let needs_drop = if let Place::Local(local) = *place { @@ -1175,7 +1198,9 @@ impl MirPass for QualifyAndPromoteConstants { let (temps, candidates) = { let mut qualifier = Qualifier::new(tcx, def_id, mir, mode); if mode == Mode::ConstFn { - if tcx.is_min_const_fn(def_id) { + if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you { + qualifier.qualify_const(); + } else if tcx.is_min_const_fn(def_id) { // enforce `min_const_fn` for stable const fns use super::qualify_min_const_fn::is_min_const_fn; if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) { diff --git a/src/test/ui/consts/miri_unleashed/assoc_const.rs b/src/test/ui/consts/miri_unleashed/assoc_const.rs new file mode 100644 index 0000000000000..b8959667cc215 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/assoc_const.rs @@ -0,0 +1,30 @@ +// compile-flags: -Zunleash-the-miri-inside-of-you +#![allow(const_err)] + +// a test demonstrating why we do need to run static const qualification on associated constants +// instead of just checking the final constant + +trait Foo { + const X: T; +} + +trait Bar> { + const F: u32 = (U::X, 42).1; //~ WARN skipping const checks +} + +impl Foo for () { + const X: u32 = 42; +} +impl Foo> for String { + const X: Vec = Vec::new(); +} + +impl Bar for () {} +impl Bar, String> for String {} + +fn main() { + // this is fine, but would have been forbidden by the static checks on `F` + let x = <() as Bar>::F; + // this test only causes errors due to the line below, so post-monomorphization + let y = , String>>::F; //~ ERROR erroneous constant +} diff --git a/src/test/ui/consts/miri_unleashed/assoc_const.stderr b/src/test/ui/consts/miri_unleashed/assoc_const.stderr new file mode 100644 index 0000000000000..a40f8d46d0aa7 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/assoc_const.stderr @@ -0,0 +1,15 @@ +warning: skipping const checks + --> $DIR/assoc_const.rs:12:31 + | +LL | const F: u32 = (U::X, 42).1; //~ WARN skipping const checks + | ^ + +error[E0080]: erroneous constant used + --> $DIR/assoc_const.rs:29:13 + | +LL | let y = , String>>::F; //~ ERROR erroneous constant + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/assoc_const_2.rs b/src/test/ui/consts/miri_unleashed/assoc_const_2.rs new file mode 100644 index 0000000000000..c87b6389848f4 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/assoc_const_2.rs @@ -0,0 +1,28 @@ +#![allow(const_err)] + +// a test demonstrating that const qualification cannot prevent monomorphization time errors + +trait Foo { + const X: u32; +} + +trait Bar { + const F: u32 = 100 / U::X; +} + +impl Foo for () { + const X: u32 = 42; +} + +impl Foo for String { + const X: u32 = 0; +} + +impl Bar<()> for () {} +impl Bar for String {} + +fn main() { + let x = <() as Bar<()>>::F; + // this test only causes errors due to the line below, so post-monomorphization + let y = >::F; //~ ERROR erroneous constant +} diff --git a/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr b/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr new file mode 100644 index 0000000000000..77aab31d26ec3 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/assoc_const_2.stderr @@ -0,0 +1,9 @@ +error[E0080]: erroneous constant used + --> $DIR/assoc_const_2.rs:27:13 + | +LL | let y = >::F; //~ ERROR erroneous constant + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.rs b/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.rs new file mode 100644 index 0000000000000..5fb92535502a5 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.rs @@ -0,0 +1,27 @@ +#![allow(const_err)] + +// a test demonstrating why we do need to run static const qualification on associated constants +// instead of just checking the final constant + +trait Foo { + const X: T; +} + +trait Bar> { + const F: u32 = (U::X, 42).1; //~ ERROR destructors cannot be evaluated at compile-time +} + +impl Foo for () { + const X: u32 = 42; +} +impl Foo> for String { + const X: Vec = Vec::new(); //~ ERROR not yet stable as a const fn +} + +impl Bar for () {} +impl Bar, String> for String {} + +fn main() { + let x = <() as Bar>::F; + let y = , String>>::F; +} diff --git a/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.stderr b/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.stderr new file mode 100644 index 0000000000000..e23ed1c620639 --- /dev/null +++ b/src/test/ui/consts/miri_unleashed/feature-gate-unleash_the_miri_inside_of_you.stderr @@ -0,0 +1,17 @@ +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/feature-gate-unleash_the_miri_inside_of_you.rs:11:20 + | +LL | const F: u32 = (U::X, 42).1; //~ ERROR destructors cannot be evaluated at compile-time + | ^^^^^^^^^^ constants cannot evaluate destructors + +error: `>::new` is not yet stable as a const fn + --> $DIR/feature-gate-unleash_the_miri_inside_of_you.rs:18:25 + | +LL | const X: Vec = Vec::new(); //~ ERROR not yet stable as a const fn + | ^^^^^^^^^^ + | + = help: add `#![feature(const_vec_new)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0493`.