diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2025-06-27 15:04:57 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-27 15:04:57 +0200 |
| commit | 0bbeeffe57378b9460ec6df4532b8a569003f7f9 (patch) | |
| tree | 81375f28340e900d1b616751535294f4c3e4062a | |
| parent | 8e7b0b57ea97afd31b05709b3da525036269df57 (diff) | |
| parent | ed4f01ed2e033098f6c5e10a4a0cc740dc04f958 (diff) | |
| download | rust-0bbeeffe57378b9460ec6df4532b8a569003f7f9.tar.gz rust-0bbeeffe57378b9460ec6df4532b8a569003f7f9.zip | |
Rollup merge of #143084 - RalfJung:const-eval-recursive-static-write, r=oli-obk
const-eval: error when initializing a static writes to that static Fixes https://github.com/rust-lang/rust/issues/142404 by also calling the relevant hook for writes, not just reads. To avoid erroring during the actual write of the initial value, we neuter the hook when popping the final stack frame. Calling the hook during writes requires changing its signature since we cannot pass in the entire interpreter any more. While doing this I also realized a gap in https://github.com/rust-lang/rust/pull/142575 for zero-sized copies on the read side, so I fixed that and added a test. r? `@oli-obk`
| -rw-r--r-- | compiler/rustc_const_eval/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 22 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/machine.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/memory.rs | 30 | ||||
| -rw-r--r-- | tests/ui/consts/recursive-static-write.rs | 24 | ||||
| -rw-r--r-- | tests/ui/consts/recursive-static-write.stderr | 15 | ||||
| -rw-r--r-- | tests/ui/consts/recursive-zst-static.default.stderr | 10 | ||||
| -rw-r--r-- | tests/ui/consts/recursive-zst-static.rs | 2 | ||||
| -rw-r--r-- | tests/ui/consts/recursive-zst-static.unleash.stderr | 10 | ||||
| -rw-r--r-- | tests/ui/consts/write-to-static-mut-in-static.rs | 3 | ||||
| -rw-r--r-- | tests/ui/consts/write-to-static-mut-in-static.stderr | 12 | ||||
| -rw-r--r-- | tests/ui/recursion/recursive-static-definition.rs | 4 | ||||
| -rw-r--r-- | tests/ui/recursion/recursive-static-definition.stderr | 4 | ||||
| -rw-r--r-- | tests/ui/statics/read_before_init.rs | 10 | ||||
| -rw-r--r-- | tests/ui/statics/read_before_init.stderr | 28 |
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`. |
