about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-06-27 12:47:47 +0200
committerRalf Jung <post@ralfj.de>2025-06-27 14:39:35 +0200
commited4f01ed2e033098f6c5e10a4a0cc740dc04f958 (patch)
tree15991ee4b0a5dfcd7d04ec09b2a48681ded67293
parente61dd437f33b5a640e67dc3628397689c664c17f (diff)
downloadrust-ed4f01ed2e033098f6c5e10a4a0cc740dc04f958.tar.gz
rust-ed4f01ed2e033098f6c5e10a4a0cc740dc04f958.zip
const-eval: error when initializing a static writes to that static
-rw-r--r--compiler/rustc_const_eval/messages.ftl2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs22
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs30
-rw-r--r--tests/ui/consts/recursive-static-write.rs24
-rw-r--r--tests/ui/consts/recursive-static-write.stderr15
-rw-r--r--tests/ui/consts/recursive-zst-static.default.stderr10
-rw-r--r--tests/ui/consts/recursive-zst-static.rs2
-rw-r--r--tests/ui/consts/recursive-zst-static.unleash.stderr10
-rw-r--r--tests/ui/consts/write-to-static-mut-in-static.rs3
-rw-r--r--tests/ui/consts/write-to-static-mut-in-static.stderr12
-rw-r--r--tests/ui/recursion/recursive-static-definition.rs4
-rw-r--r--tests/ui/recursion/recursive-static-definition.stderr4
-rw-r--r--tests/ui/statics/read_before_init.rs10
-rw-r--r--tests/ui/statics/read_before_init.stderr28
15 files changed, 133 insertions, 49 deletions
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 97b154ad142..671ae5975a7 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -352,7 +352,7 @@ const_eval_realloc_or_alloc_with_offset =
         *[other] {""}
     } {$ptr} which does not point to the beginning of an object
 
-const_eval_recursive_static = encountered static that tried to initialize itself with itself
+const_eval_recursive_static = encountered static that tried to access itself during initialization
 
 const_eval_remainder_by_zero =
     calculating the remainder with a divisor of zero
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index a68dcf29988..2ec3f8432c7 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -62,7 +62,7 @@ pub struct CompileTimeMachine<'tcx> {
 
     /// If `Some`, we are evaluating the initializer of the static with the given `LocalDefId`,
     /// storing the result in the given `AllocId`.
-    /// Used to prevent reads from a static's base allocation, as that may allow for self-initialization loops.
+    /// Used to prevent accesses to a static's base allocation, as that may allow for self-initialization loops.
     pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>,
 
     /// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes).
@@ -705,19 +705,27 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
         interp_ok(())
     }
 
-    fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> {
+    fn before_alloc_access(
+        tcx: TyCtxtAt<'tcx>,
+        machine: &Self,
+        alloc_id: AllocId,
+    ) -> InterpResult<'tcx> {
+        if machine.stack.is_empty() {
+            // Get out of the way for the final copy.
+            return interp_ok(());
+        }
         // Check if this is the currently evaluated static.
-        if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
+        if Some(alloc_id) == machine.static_root_ids.map(|(id, _)| id) {
             return Err(ConstEvalErrKind::RecursiveStatic).into();
         }
         // If this is another static, make sure we fire off the query to detect cycles.
         // But only do that when checks for static recursion are enabled.
-        if ecx.machine.static_root_ids.is_some() {
-            if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) {
-                if ecx.tcx.is_foreign_item(def_id) {
+        if machine.static_root_ids.is_some() {
+            if let Some(GlobalAlloc::Static(def_id)) = tcx.try_get_global_alloc(alloc_id) {
+                if tcx.is_foreign_item(def_id) {
                     throw_unsup!(ExternStatic(def_id));
                 }
-                ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
+                tcx.eval_static_initializer(def_id)?;
             }
         }
         interp_ok(())
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index b9e022c9604..d6d230fbd17 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -443,7 +443,11 @@ pub trait Machine<'tcx>: Sized {
     ///
     /// Used to prevent statics from self-initializing by reading from their own memory
     /// as it is being initialized.
-    fn before_alloc_read(_ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId) -> InterpResult<'tcx> {
+    fn before_alloc_access(
+        _tcx: TyCtxtAt<'tcx>,
+        _machine: &Self,
+        _alloc_id: AllocId,
+    ) -> InterpResult<'tcx> {
         interp_ok(())
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 57bf867e389..69fceb02ff9 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -720,7 +720,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
         if !self.memory.validation_in_progress.get() {
             if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
-                M::before_alloc_read(self, alloc_id)?;
+                M::before_alloc_access(self.tcx, &self.machine, alloc_id)?;
             }
         }
 
@@ -821,6 +821,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         if let Some((alloc_id, offset, prov, alloc, machine)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
             if !validation_in_progress {
+                // For writes, it's okay to only call those when there actually is a non-zero
+                // amount of bytes to be written: a zero-sized write doesn't manifest anything.
+                M::before_alloc_access(tcx, machine, alloc_id)?;
                 M::before_memory_write(
                     tcx,
                     machine,
@@ -1396,6 +1399,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let src_parts = self.get_ptr_access(src, size)?;
         let dest_parts = self.get_ptr_access(dest, size * num_copies)?; // `Size` multiplication
 
+        // Similar to `get_ptr_alloc`, we need to call `before_alloc_access` even for zero-sized
+        // reads. However, just like in `get_ptr_alloc_mut`, the write part is okay to skip for
+        // zero-sized writes.
+        if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes().try_into().unwrap())
+        {
+            M::before_alloc_access(tcx, &self.machine, alloc_id)?;
+        }
+
         // FIXME: we look up both allocations twice here, once before for the `check_ptr_access`
         // and once below to get the underlying `&[mut] Allocation`.
 
@@ -1408,12 +1419,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let src_range = alloc_range(src_offset, size);
         assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
 
-        // Trigger read hooks.
-        // For the overlapping case, it is crucial that we trigger the read hooks
+        // Trigger read hook.
+        // For the overlapping case, it is crucial that we trigger the read hook
         // before the write hook -- the aliasing model cares about the order.
-        if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes() as i64) {
-            M::before_alloc_read(self, alloc_id)?;
-        }
         M::before_memory_read(
             tcx,
             &self.machine,
@@ -1438,16 +1446,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let provenance = src_alloc
             .provenance()
             .prepare_copy(src_range, dest_offset, num_copies, self)
-            .map_err(|e| e.to_interp_error(dest_alloc_id))?;
+            .map_err(|e| e.to_interp_error(src_alloc_id))?;
         // Prepare a copy of the initialization mask.
         let init = src_alloc.init_mask().prepare_copy(src_range);
 
-        // Destination alloc preparations and access hooks.
-        let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
+        // Destination alloc preparations...
+        let (dest_alloc, machine) = self.get_alloc_raw_mut(dest_alloc_id)?;
         let dest_range = alloc_range(dest_offset, size * num_copies);
+        // ...and access hooks.
+        M::before_alloc_access(tcx, machine, dest_alloc_id)?;
         M::before_memory_write(
             tcx,
-            extra,
+            machine,
             &mut dest_alloc.extra,
             dest,
             (dest_alloc_id, dest_prov),
diff --git a/tests/ui/consts/recursive-static-write.rs b/tests/ui/consts/recursive-static-write.rs
new file mode 100644
index 00000000000..dc5813d8c78
--- /dev/null
+++ b/tests/ui/consts/recursive-static-write.rs
@@ -0,0 +1,24 @@
+//! Ensure that writing to `S` while initializing `S` errors.
+//! Regression test for <https://github.com/rust-lang/rust/issues/142404>.
+#![allow(dead_code)]
+
+struct Foo {
+    x: i32,
+    y: (),
+}
+
+static S: Foo = Foo {
+    x: 0,
+    y: unsafe {
+        (&raw const S.x).cast_mut().write(1); //~ERROR access itself during initialization
+    },
+};
+
+static mut S2: Foo = Foo {
+    x: 0,
+    y: unsafe {
+        S2.x = 1; //~ERROR access itself during initialization
+    },
+};
+
+fn main() {}
diff --git a/tests/ui/consts/recursive-static-write.stderr b/tests/ui/consts/recursive-static-write.stderr
new file mode 100644
index 00000000000..f5b5c49317c
--- /dev/null
+++ b/tests/ui/consts/recursive-static-write.stderr
@@ -0,0 +1,15 @@
+error[E0080]: encountered static that tried to access itself during initialization
+  --> $DIR/recursive-static-write.rs:13:9
+   |
+LL |         (&raw const S.x).cast_mut().write(1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `S` failed here
+
+error[E0080]: encountered static that tried to access itself during initialization
+  --> $DIR/recursive-static-write.rs:20:9
+   |
+LL |         S2.x = 1;
+   |         ^^^^^^^^ evaluation of `S2` failed here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/recursive-zst-static.default.stderr b/tests/ui/consts/recursive-zst-static.default.stderr
index fee33a892d0..c814576dfd5 100644
--- a/tests/ui/consts/recursive-zst-static.default.stderr
+++ b/tests/ui/consts/recursive-zst-static.default.stderr
@@ -1,20 +1,20 @@
-error[E0080]: encountered static that tried to initialize itself with itself
+error[E0080]: encountered static that tried to access itself during initialization
   --> $DIR/recursive-zst-static.rs:10:18
    |
 LL | static FOO: () = FOO;
    |                  ^^^ evaluation of `FOO` failed here
 
 error[E0391]: cycle detected when evaluating initializer of static `A`
-  --> $DIR/recursive-zst-static.rs:13:16
+  --> $DIR/recursive-zst-static.rs:13:1
    |
 LL | static A: () = B;
-   |                ^
+   | ^^^^^^^^^^^^
    |
 note: ...which requires evaluating initializer of static `B`...
-  --> $DIR/recursive-zst-static.rs:14:16
+  --> $DIR/recursive-zst-static.rs:14:1
    |
 LL | static B: () = A;
-   |                ^
+   | ^^^^^^^^^^^^
    = note: ...which again requires evaluating initializer of static `A`, completing the cycle
    = note: cycle used when running analysis passes on this crate
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
diff --git a/tests/ui/consts/recursive-zst-static.rs b/tests/ui/consts/recursive-zst-static.rs
index 852caae9493..853af6d70eb 100644
--- a/tests/ui/consts/recursive-zst-static.rs
+++ b/tests/ui/consts/recursive-zst-static.rs
@@ -8,7 +8,7 @@
 // See https://github.com/rust-lang/rust/issues/71078 for more details.
 
 static FOO: () = FOO;
-//~^ ERROR encountered static that tried to initialize itself with itself
+//~^ ERROR encountered static that tried to access itself during initialization
 
 static A: () = B; //~ ERROR cycle detected when evaluating initializer of static `A`
 static B: () = A;
diff --git a/tests/ui/consts/recursive-zst-static.unleash.stderr b/tests/ui/consts/recursive-zst-static.unleash.stderr
index fee33a892d0..c814576dfd5 100644
--- a/tests/ui/consts/recursive-zst-static.unleash.stderr
+++ b/tests/ui/consts/recursive-zst-static.unleash.stderr
@@ -1,20 +1,20 @@
-error[E0080]: encountered static that tried to initialize itself with itself
+error[E0080]: encountered static that tried to access itself during initialization
   --> $DIR/recursive-zst-static.rs:10:18
    |
 LL | static FOO: () = FOO;
    |                  ^^^ evaluation of `FOO` failed here
 
 error[E0391]: cycle detected when evaluating initializer of static `A`
-  --> $DIR/recursive-zst-static.rs:13:16
+  --> $DIR/recursive-zst-static.rs:13:1
    |
 LL | static A: () = B;
-   |                ^
+   | ^^^^^^^^^^^^
    |
 note: ...which requires evaluating initializer of static `B`...
-  --> $DIR/recursive-zst-static.rs:14:16
+  --> $DIR/recursive-zst-static.rs:14:1
    |
 LL | static B: () = A;
-   |                ^
+   | ^^^^^^^^^^^^
    = note: ...which again requires evaluating initializer of static `A`, completing the cycle
    = note: cycle used when running analysis passes on this crate
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
diff --git a/tests/ui/consts/write-to-static-mut-in-static.rs b/tests/ui/consts/write-to-static-mut-in-static.rs
index ce15d9e912b..016bfb06ccf 100644
--- a/tests/ui/consts/write-to-static-mut-in-static.rs
+++ b/tests/ui/consts/write-to-static-mut-in-static.rs
@@ -3,8 +3,9 @@ pub static mut B: () = unsafe { A = 1; };
 //~^ ERROR modifying a static's initial value
 
 pub static mut C: u32 = unsafe { C = 1; 0 };
+//~^ ERROR static that tried to access itself during initialization
 
 pub static D: u32 = D;
-//~^ ERROR static that tried to initialize itself with itself
+//~^ ERROR static that tried to access itself during initialization
 
 fn main() {}
diff --git a/tests/ui/consts/write-to-static-mut-in-static.stderr b/tests/ui/consts/write-to-static-mut-in-static.stderr
index bb5e217afb9..4180bb49339 100644
--- a/tests/ui/consts/write-to-static-mut-in-static.stderr
+++ b/tests/ui/consts/write-to-static-mut-in-static.stderr
@@ -4,12 +4,18 @@ error[E0080]: modifying a static's initial value from another static's initializ
 LL | pub static mut B: () = unsafe { A = 1; };
    |                                 ^^^^^ evaluation of `B` failed here
 
-error[E0080]: encountered static that tried to initialize itself with itself
-  --> $DIR/write-to-static-mut-in-static.rs:7:21
+error[E0080]: encountered static that tried to access itself during initialization
+  --> $DIR/write-to-static-mut-in-static.rs:5:34
+   |
+LL | pub static mut C: u32 = unsafe { C = 1; 0 };
+   |                                  ^^^^^ evaluation of `C` failed here
+
+error[E0080]: encountered static that tried to access itself during initialization
+  --> $DIR/write-to-static-mut-in-static.rs:8:21
    |
 LL | pub static D: u32 = D;
    |                     ^ evaluation of `D` failed here
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/recursion/recursive-static-definition.rs b/tests/ui/recursion/recursive-static-definition.rs
index 55db6a86bf1..4f0624eb162 100644
--- a/tests/ui/recursion/recursive-static-definition.rs
+++ b/tests/ui/recursion/recursive-static-definition.rs
@@ -1,5 +1,5 @@
 pub static FOO: u32 = FOO;
-//~^ ERROR encountered static that tried to initialize itself with itself
+//~^ ERROR encountered static that tried to access itself during initialization
 
 #[derive(Copy, Clone)]
 pub union Foo {
@@ -7,6 +7,6 @@ pub union Foo {
 }
 
 pub static BAR: Foo = BAR;
-//~^ ERROR encountered static that tried to initialize itself with itself
+//~^ ERROR encountered static that tried to access itself during initialization
 
 fn main() {}
diff --git a/tests/ui/recursion/recursive-static-definition.stderr b/tests/ui/recursion/recursive-static-definition.stderr
index ce93c41bc67..1e4005832cb 100644
--- a/tests/ui/recursion/recursive-static-definition.stderr
+++ b/tests/ui/recursion/recursive-static-definition.stderr
@@ -1,10 +1,10 @@
-error[E0080]: encountered static that tried to initialize itself with itself
+error[E0080]: encountered static that tried to access itself during initialization
   --> $DIR/recursive-static-definition.rs:1:23
    |
 LL | pub static FOO: u32 = FOO;
    |                       ^^^ evaluation of `FOO` failed here
 
-error[E0080]: encountered static that tried to initialize itself with itself
+error[E0080]: encountered static that tried to access itself during initialization
   --> $DIR/recursive-static-definition.rs:9:23
    |
 LL | pub static BAR: Foo = BAR;
diff --git a/tests/ui/statics/read_before_init.rs b/tests/ui/statics/read_before_init.rs
index d779ef6dffa..32cc2554e1a 100644
--- a/tests/ui/statics/read_before_init.rs
+++ b/tests/ui/statics/read_before_init.rs
@@ -8,13 +8,15 @@
 
 use std::mem::MaybeUninit;
 
-pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
-//~^ ERROR: encountered static that tried to initialize itself with itself
+pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0, 1));
+//~^ ERROR: encountered static that tried to access itself during initialization
+pub static Y: (i32, MaybeUninit<i32>) = (1, foo(&Y.0, 0));
+//~^ ERROR: encountered static that tried to access itself during initialization
 
-const fn foo(x: &i32) -> MaybeUninit<i32> {
+const fn foo(x: &i32, num: usize) -> MaybeUninit<i32> {
     let mut temp = MaybeUninit::<i32>::uninit();
     unsafe {
-        std::ptr::copy(x, temp.as_mut_ptr(), 1);
+        std::ptr::copy(x, temp.as_mut_ptr(), num);
     }
     temp
 }
diff --git a/tests/ui/statics/read_before_init.stderr b/tests/ui/statics/read_before_init.stderr
index aeebcf7d9ce..239568c1205 100644
--- a/tests/ui/statics/read_before_init.stderr
+++ b/tests/ui/statics/read_before_init.stderr
@@ -1,17 +1,31 @@
-error[E0080]: encountered static that tried to initialize itself with itself
+error[E0080]: encountered static that tried to access itself during initialization
   --> $DIR/read_before_init.rs:11:45
    |
-LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
-   |                                             ^^^^^^^^^ evaluation of `X` failed inside this call
+LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0, 1));
+   |                                             ^^^^^^^^^^^^ evaluation of `X` failed inside this call
    |
 note: inside `foo`
-  --> $DIR/read_before_init.rs:17:9
+  --> $DIR/read_before_init.rs:19:9
    |
-LL |         std::ptr::copy(x, temp.as_mut_ptr(), 1);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         std::ptr::copy(x, temp.as_mut_ptr(), num);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: inside `std::ptr::copy::<i32>`
   --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
 
-error: aborting due to 1 previous error
+error[E0080]: encountered static that tried to access itself during initialization
+  --> $DIR/read_before_init.rs:13:45
+   |
+LL | pub static Y: (i32, MaybeUninit<i32>) = (1, foo(&Y.0, 0));
+   |                                             ^^^^^^^^^^^^ evaluation of `Y` failed inside this call
+   |
+note: inside `foo`
+  --> $DIR/read_before_init.rs:19:9
+   |
+LL |         std::ptr::copy(x, temp.as_mut_ptr(), num);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: inside `std::ptr::copy::<i32>`
+  --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0080`.