about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-03-02 10:54:37 +0100
committerRalf Jung <post@ralfj.de>2024-03-11 14:15:24 +0100
commit279465b5e884da308a8d0b866cade7bdbfb9f19f (patch)
treec06b9c835cec07d2bf38697e2c0d171c1cc759e8
parentd9a2886ef8ba8716abbaad338d1e7278d66d25b8 (diff)
downloadrust-279465b5e884da308a8d0b866cade7bdbfb9f19f.tar.gz
rust-279465b5e884da308a8d0b866cade7bdbfb9f19f.zip
const-checking: add some corner case tests, and fix some nits
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs2
-rw-r--r--tests/ui/consts/enclosing-scope-rule.rs16
-rw-r--r--tests/ui/consts/refs-to-cell-in-final.rs23
-rw-r--r--tests/ui/consts/refs-to-cell-in-final.stderr8
5 files changed, 51 insertions, 6 deletions
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 9e4e7911c3a..c18b5b795d4 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 c98dc4deb75..7e8e38a9653 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<QualifResults<'mir, 'tcx, HasMutInterior>>,
     needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
     needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
diff --git a/tests/ui/consts/enclosing-scope-rule.rs b/tests/ui/consts/enclosing-scope-rule.rs
new file mode 100644
index 00000000000..7041ad9af34
--- /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>(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<i32> = &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 6a849ff0df9..ada56a82a5d 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<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
 const RAW_SYNC_C: SyncPtr<Cell<i32>> = 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
+// <https://github.com/rust-lang/rust/issues/121610>. 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<bool>),
+}
+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<Cell<i32>> = &None;
+// Making it clear that this is promotion, not "outer scope".
+const NONE_EXPLICIT_PROMOTED: &'static Option<Cell<i32>> = {
+    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 fae16fa0125..b06db3e116c 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<Cell<i32>> = 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`.