From 279465b5e884da308a8d0b866cade7bdbfb9f19f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 2 Mar 2024 10:54:37 +0100 Subject: [PATCH] const-checking: add some corner case tests, and fix some nits --- .../src/const_eval/eval_queries.rs | 8 +++---- .../src/transform/check_consts/check.rs | 2 +- tests/ui/consts/enclosing-scope-rule.rs | 16 +++++++++++++ tests/ui/consts/refs-to-cell-in-final.rs | 23 +++++++++++++++++++ tests/ui/consts/refs-to-cell-in-final.stderr | 8 ++++++- 5 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/ui/consts/enclosing-scope-rule.rs diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 9e4e7911c3a7..c18b5b795d42 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -412,10 +412,10 @@ pub fn const_validate_mplace<'mir, 'tcx>( _ if cid.promoted.is_some() => CtfeValidationMode::Promoted, Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static` None => { - // In normal `const` (not promoted), the outermost allocation is always only copied, - // so having `UnsafeCell` in there is okay despite them being in immutable memory. - let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner; - CtfeValidationMode::Const { allow_immutable_unsafe_cell } + // This is a normal `const` (not promoted). + // The outermost allocation is always only copied, so having `UnsafeCell` in there + // is okay despite them being in immutable memory. + CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner } } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index c98dc4deb757..7e8e38a96534 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -30,7 +30,7 @@ type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; #[derive(Default)] -pub struct Qualifs<'mir, 'tcx> { +pub(crate) struct Qualifs<'mir, 'tcx> { has_mut_interior: Option>, needs_drop: Option>, needs_non_const_drop: Option>, diff --git a/tests/ui/consts/enclosing-scope-rule.rs b/tests/ui/consts/enclosing-scope-rule.rs new file mode 100644 index 000000000000..7041ad9af34e --- /dev/null +++ b/tests/ui/consts/enclosing-scope-rule.rs @@ -0,0 +1,16 @@ +//@build-pass +// Some code that looks like it might be relying on promotion, but actually this is using the +// enclosing-scope rule, meaning the reference is "extended" to outlive its block and live as long +// as the surrounding block (which in this case is the entire program). There are multiple +// allocations being interned at once. + +struct Gen(T); +impl<'a, T> Gen<&'a T> { + // Can't be promoted because `T` might not be `'static`. + const C: &'a [T] = &[]; +} + +// Can't be promoted because of `Drop`. +const V: &Vec = &Vec::new(); + +fn main() {} diff --git a/tests/ui/consts/refs-to-cell-in-final.rs b/tests/ui/consts/refs-to-cell-in-final.rs index 6a849ff0df92..ada56a82a5d1 100644 --- a/tests/ui/consts/refs-to-cell-in-final.rs +++ b/tests/ui/consts/refs-to-cell-in-final.rs @@ -15,4 +15,27 @@ static RAW_SYNC_S: SyncPtr> = SyncPtr { x: &Cell::new(42) }; const RAW_SYNC_C: SyncPtr> = SyncPtr { x: &Cell::new(42) }; //~^ ERROR: cannot refer to interior mutable data +// This one does not get promoted because of `Drop`, and then enters interesting codepaths because +// as a value it has no interior mutability, but as a type it does. See +// . Value-based reasoning for interior mutability +// is questionable (https://github.com/rust-lang/unsafe-code-guidelines/issues/493) so for now we +// reject this, though not with a great error message. +pub enum JsValue { + Undefined, + Object(Cell), +} +impl Drop for JsValue { + fn drop(&mut self) {} +} +const UNDEFINED: &JsValue = &JsValue::Undefined; +//~^ERROR: mutable pointer in final value of constant + +// In contrast, this one works since it is being promoted. +const NONE: &'static Option> = &None; +// Making it clear that this is promotion, not "outer scope". +const NONE_EXPLICIT_PROMOTED: &'static Option> = { + let x = &None; + x +}; + fn main() {} diff --git a/tests/ui/consts/refs-to-cell-in-final.stderr b/tests/ui/consts/refs-to-cell-in-final.stderr index fae16fa01251..b06db3e116ce 100644 --- a/tests/ui/consts/refs-to-cell-in-final.stderr +++ b/tests/ui/consts/refs-to-cell-in-final.stderr @@ -12,6 +12,12 @@ error[E0492]: constants cannot refer to interior mutable data LL | const RAW_SYNC_C: SyncPtr> = SyncPtr { x: &Cell::new(42) }; | ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value -error: aborting due to 2 previous errors +error: encountered mutable pointer in final value of constant + --> $DIR/refs-to-cell-in-final.rs:30:1 + | +LL | const UNDEFINED: &JsValue = &JsValue::Undefined; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0492`.