Add a forever unstable opt-out of const qualification checks
This commit is contained in:
parent
f40aaa6827
commit
d0129a613c
8 changed files with 155 additions and 2 deletions
|
@ -1300,6 +1300,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||||
"print some statistics about AST and HIR"),
|
"print some statistics about AST and HIR"),
|
||||||
always_encode_mir: bool = (false, parse_bool, [TRACKED],
|
always_encode_mir: bool = (false, parse_bool, [TRACKED],
|
||||||
"encode MIR of all functions into the crate metadata"),
|
"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],
|
osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],
|
||||||
"pass `-install_name @rpath/...` to the macOS linker"),
|
"pass `-install_name @rpath/...` to the macOS linker"),
|
||||||
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
|
sanitizer: Option<Sanitizer> = (None, parse_sanitizer, [TRACKED],
|
||||||
|
|
|
@ -108,6 +108,15 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||||
promotion_candidates: Vec<Candidate>
|
promotion_candidates: Vec<Candidate>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
||||||
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
|
@ -147,6 +156,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
|
||||||
// categories, but enabling full miri would make that
|
// categories, but enabling full miri would make that
|
||||||
// slightly pointless (even with feature-gating).
|
// slightly pointless (even with feature-gating).
|
||||||
fn not_const(&mut self) {
|
fn not_const(&mut self) {
|
||||||
|
unleash_miri!(self);
|
||||||
self.add(Qualif::NOT_CONST);
|
self.add(Qualif::NOT_CONST);
|
||||||
if self.mode != Mode::Fn {
|
if self.mode != Mode::Fn {
|
||||||
let mut err = struct_span_err!(
|
let mut err = struct_span_err!(
|
||||||
|
@ -419,6 +429,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
unleash_miri!(self);
|
||||||
self.add(Qualif::NOT_CONST);
|
self.add(Qualif::NOT_CONST);
|
||||||
|
|
||||||
if self.mode != Mode::Fn {
|
if self.mode != Mode::Fn {
|
||||||
|
@ -618,6 +629,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if forbidden_mut {
|
if forbidden_mut {
|
||||||
|
unleash_miri!(self);
|
||||||
self.add(Qualif::NOT_CONST);
|
self.add(Qualif::NOT_CONST);
|
||||||
if self.mode != Mode::Fn {
|
if self.mode != Mode::Fn {
|
||||||
let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
|
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);
|
debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
|
||||||
if forbidden_mut {
|
if forbidden_mut {
|
||||||
|
unleash_miri!(self);
|
||||||
self.add(Qualif::NOT_CONST);
|
self.add(Qualif::NOT_CONST);
|
||||||
} else {
|
} else {
|
||||||
// We might have a candidate for promotion.
|
// 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) {
|
match (cast_in, cast_out) {
|
||||||
(CastTy::Ptr(_), CastTy::Int(_)) |
|
(CastTy::Ptr(_), CastTy::Int(_)) |
|
||||||
(CastTy::FnPtr, CastTy::Int(_)) => {
|
(CastTy::FnPtr, CastTy::Int(_)) => {
|
||||||
|
unleash_miri!(self);
|
||||||
if let Mode::Fn = self.mode {
|
if let Mode::Fn = self.mode {
|
||||||
// in normal functions, mark such casts as not promotable
|
// in normal functions, mark such casts as not promotable
|
||||||
self.add(Qualif::NOT_CONST);
|
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::Ge || op == BinOp::Gt ||
|
||||||
op == BinOp::Offset);
|
op == BinOp::Offset);
|
||||||
|
|
||||||
|
unleash_miri!(self);
|
||||||
if let Mode::Fn = self.mode {
|
if let Mode::Fn = self.mode {
|
||||||
// raw pointer operations are not allowed inside promoteds
|
// raw pointer operations are not allowed inside promoteds
|
||||||
self.add(Qualif::NOT_CONST);
|
self.add(Qualif::NOT_CONST);
|
||||||
|
@ -745,6 +760,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Rvalue::NullaryOp(NullOp::Box, _) => {
|
Rvalue::NullaryOp(NullOp::Box, _) => {
|
||||||
|
unleash_miri!(self);
|
||||||
self.add(Qualif::NOT_CONST);
|
self.add(Qualif::NOT_CONST);
|
||||||
if self.mode != Mode::Fn {
|
if self.mode != Mode::Fn {
|
||||||
let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
|
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 {
|
} else {
|
||||||
// stable const fns or unstable const fns with their feature gate
|
// stable const fns or unstable const fns with their feature gate
|
||||||
// active
|
// 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;
|
is_const_fn = true;
|
||||||
} else if self.is_const_panic_fn(def_id) {
|
} else if self.is_const_panic_fn(def_id) {
|
||||||
// Check the const_panic feature gate.
|
// 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.
|
// Deny *any* live drops anywhere other than functions.
|
||||||
if self.mode != Mode::Fn {
|
if self.mode != Mode::Fn {
|
||||||
|
unleash_miri!(self);
|
||||||
// HACK(eddyb): emulate a bit of dataflow analysis,
|
// HACK(eddyb): emulate a bit of dataflow analysis,
|
||||||
// conservatively, that drop elaboration will do.
|
// conservatively, that drop elaboration will do.
|
||||||
let needs_drop = if let Place::Local(local) = *place {
|
let needs_drop = if let Place::Local(local) = *place {
|
||||||
|
@ -1175,7 +1198,9 @@ impl MirPass for QualifyAndPromoteConstants {
|
||||||
let (temps, candidates) = {
|
let (temps, candidates) = {
|
||||||
let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
|
let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
|
||||||
if mode == Mode::ConstFn {
|
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
|
// enforce `min_const_fn` for stable const fns
|
||||||
use super::qualify_min_const_fn::is_min_const_fn;
|
use super::qualify_min_const_fn::is_min_const_fn;
|
||||||
if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
|
if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
|
||||||
|
|
30
src/test/ui/consts/miri_unleashed/assoc_const.rs
Normal file
30
src/test/ui/consts/miri_unleashed/assoc_const.rs
Normal file
|
@ -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<T> {
|
||||||
|
const X: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar<T, U: Foo<T>> {
|
||||||
|
const F: u32 = (U::X, 42).1; //~ WARN skipping const checks
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo<u32> for () {
|
||||||
|
const X: u32 = 42;
|
||||||
|
}
|
||||||
|
impl Foo<Vec<u32>> for String {
|
||||||
|
const X: Vec<u32> = Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bar<u32, ()> for () {}
|
||||||
|
impl Bar<Vec<u32>, String> for String {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// this is fine, but would have been forbidden by the static checks on `F`
|
||||||
|
let x = <() as Bar<u32, ()>>::F;
|
||||||
|
// this test only causes errors due to the line below, so post-monomorphization
|
||||||
|
let y = <String as Bar<Vec<u32>, String>>::F; //~ ERROR erroneous constant
|
||||||
|
}
|
15
src/test/ui/consts/miri_unleashed/assoc_const.stderr
Normal file
15
src/test/ui/consts/miri_unleashed/assoc_const.stderr
Normal file
|
@ -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 as Bar<Vec<u32>, 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`.
|
28
src/test/ui/consts/miri_unleashed/assoc_const_2.rs
Normal file
28
src/test/ui/consts/miri_unleashed/assoc_const_2.rs
Normal file
|
@ -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<U: Foo> {
|
||||||
|
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<String> for String {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = <() as Bar<()>>::F;
|
||||||
|
// this test only causes errors due to the line below, so post-monomorphization
|
||||||
|
let y = <String as Bar<String>>::F; //~ ERROR erroneous constant
|
||||||
|
}
|
9
src/test/ui/consts/miri_unleashed/assoc_const_2.stderr
Normal file
9
src/test/ui/consts/miri_unleashed/assoc_const_2.stderr
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
error[E0080]: erroneous constant used
|
||||||
|
--> $DIR/assoc_const_2.rs:27:13
|
||||||
|
|
|
||||||
|
LL | let y = <String as Bar<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`.
|
|
@ -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<T> {
|
||||||
|
const X: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar<T, U: Foo<T>> {
|
||||||
|
const F: u32 = (U::X, 42).1; //~ ERROR destructors cannot be evaluated at compile-time
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo<u32> for () {
|
||||||
|
const X: u32 = 42;
|
||||||
|
}
|
||||||
|
impl Foo<Vec<u32>> for String {
|
||||||
|
const X: Vec<u32> = Vec::new(); //~ ERROR not yet stable as a const fn
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bar<u32, ()> for () {}
|
||||||
|
impl Bar<Vec<u32>, String> for String {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = <() as Bar<u32, ()>>::F;
|
||||||
|
let y = <String as Bar<Vec<u32>, String>>::F;
|
||||||
|
}
|
|
@ -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: `<std::vec::Vec<T>>::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<u32> = 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`.
|
Loading…
Reference in a new issue