diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 97b154ad14235..671ae5975a78b 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 a68dcf2998866..2ec3f8432c771 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 b9e022c96043f..d6d230fbd1776 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 57bf867e389b2..69fceb02ff931 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 0000000000000..dc5813d8c7834 --- /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 . +#![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 0000000000000..f5b5c49317cfc --- /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 fee33a892d06e..c814576dfd5b9 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 852caae949341..853af6d70eb09 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 fee33a892d06e..c814576dfd5b9 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 ce15d9e912b64..016bfb06ccfc7 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 bb5e217afb97b..4180bb49339e2 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 55db6a86bf1fc..4f0624eb16235 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 ce93c41bc67ce..1e4005832cbba 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 d779ef6dffab2..32cc2554e1a16 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) = (1, foo(&X.0)); -//~^ ERROR: encountered static that tried to initialize itself with itself +pub static X: (i32, MaybeUninit) = (1, foo(&X.0, 1)); +//~^ ERROR: encountered static that tried to access itself during initialization +pub static Y: (i32, MaybeUninit) = (1, foo(&Y.0, 0)); +//~^ ERROR: encountered static that tried to access itself during initialization -const fn foo(x: &i32) -> MaybeUninit { +const fn foo(x: &i32, num: usize) -> MaybeUninit { let mut temp = MaybeUninit::::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 aeebcf7d9ce5a..239568c12050c 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) = (1, foo(&X.0)); - | ^^^^^^^^^ evaluation of `X` failed inside this call +LL | pub static X: (i32, MaybeUninit) = (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::` --> $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) = (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::` + --> $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`.