Skip to content

Commit 9e965fb

Browse files
authored
Unrolled build for #143084
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 #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 #142575 for zero-sized copies on the read side, so I fixed that and added a test. r? `@oli-obk`
2 parents 13c46fd + ed4f01e commit 9e965fb

15 files changed

+133
-49
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ const_eval_realloc_or_alloc_with_offset =
352352
*[other] {""}
353353
} {$ptr} which does not point to the beginning of an object
354354
355-
const_eval_recursive_static = encountered static that tried to initialize itself with itself
355+
const_eval_recursive_static = encountered static that tried to access itself during initialization
356356
357357
const_eval_remainder_by_zero =
358358
calculating the remainder with a divisor of zero

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub struct CompileTimeMachine<'tcx> {
6262

6363
/// If `Some`, we are evaluating the initializer of the static with the given `LocalDefId`,
6464
/// storing the result in the given `AllocId`.
65-
/// Used to prevent reads from a static's base allocation, as that may allow for self-initialization loops.
65+
/// Used to prevent accesses to a static's base allocation, as that may allow for self-initialization loops.
6666
pub(crate) static_root_ids: Option<(AllocId, LocalDefId)>,
6767

6868
/// 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> {
705705
interp_ok(())
706706
}
707707

708-
fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> {
708+
fn before_alloc_access(
709+
tcx: TyCtxtAt<'tcx>,
710+
machine: &Self,
711+
alloc_id: AllocId,
712+
) -> InterpResult<'tcx> {
713+
if machine.stack.is_empty() {
714+
// Get out of the way for the final copy.
715+
return interp_ok(());
716+
}
709717
// Check if this is the currently evaluated static.
710-
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
718+
if Some(alloc_id) == machine.static_root_ids.map(|(id, _)| id) {
711719
return Err(ConstEvalErrKind::RecursiveStatic).into();
712720
}
713721
// If this is another static, make sure we fire off the query to detect cycles.
714722
// But only do that when checks for static recursion are enabled.
715-
if ecx.machine.static_root_ids.is_some() {
716-
if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) {
717-
if ecx.tcx.is_foreign_item(def_id) {
723+
if machine.static_root_ids.is_some() {
724+
if let Some(GlobalAlloc::Static(def_id)) = tcx.try_get_global_alloc(alloc_id) {
725+
if tcx.is_foreign_item(def_id) {
718726
throw_unsup!(ExternStatic(def_id));
719727
}
720-
ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
728+
tcx.eval_static_initializer(def_id)?;
721729
}
722730
}
723731
interp_ok(())

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,11 @@ pub trait Machine<'tcx>: Sized {
443443
///
444444
/// Used to prevent statics from self-initializing by reading from their own memory
445445
/// as it is being initialized.
446-
fn before_alloc_read(_ecx: &InterpCx<'tcx, Self>, _alloc_id: AllocId) -> InterpResult<'tcx> {
446+
fn before_alloc_access(
447+
_tcx: TyCtxtAt<'tcx>,
448+
_machine: &Self,
449+
_alloc_id: AllocId,
450+
) -> InterpResult<'tcx> {
447451
interp_ok(())
448452
}
449453

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
720720
// do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
721721
if !self.memory.validation_in_progress.get() {
722722
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr, size_i64) {
723-
M::before_alloc_read(self, alloc_id)?;
723+
M::before_alloc_access(self.tcx, &self.machine, alloc_id)?;
724724
}
725725
}
726726

@@ -821,6 +821,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
821821
if let Some((alloc_id, offset, prov, alloc, machine)) = ptr_and_alloc {
822822
let range = alloc_range(offset, size);
823823
if !validation_in_progress {
824+
// For writes, it's okay to only call those when there actually is a non-zero
825+
// amount of bytes to be written: a zero-sized write doesn't manifest anything.
826+
M::before_alloc_access(tcx, machine, alloc_id)?;
824827
M::before_memory_write(
825828
tcx,
826829
machine,
@@ -1396,6 +1399,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
13961399
let src_parts = self.get_ptr_access(src, size)?;
13971400
let dest_parts = self.get_ptr_access(dest, size * num_copies)?; // `Size` multiplication
13981401

1402+
// Similar to `get_ptr_alloc`, we need to call `before_alloc_access` even for zero-sized
1403+
// reads. However, just like in `get_ptr_alloc_mut`, the write part is okay to skip for
1404+
// zero-sized writes.
1405+
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes().try_into().unwrap())
1406+
{
1407+
M::before_alloc_access(tcx, &self.machine, alloc_id)?;
1408+
}
1409+
13991410
// FIXME: we look up both allocations twice here, once before for the `check_ptr_access`
14001411
// and once below to get the underlying `&[mut] Allocation`.
14011412

@@ -1408,12 +1419,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
14081419
let src_range = alloc_range(src_offset, size);
14091420
assert!(!self.memory.validation_in_progress.get(), "we can't be copying during validation");
14101421

1411-
// Trigger read hooks.
1412-
// For the overlapping case, it is crucial that we trigger the read hooks
1422+
// Trigger read hook.
1423+
// For the overlapping case, it is crucial that we trigger the read hook
14131424
// before the write hook -- the aliasing model cares about the order.
1414-
if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(src, size.bytes() as i64) {
1415-
M::before_alloc_read(self, alloc_id)?;
1416-
}
14171425
M::before_memory_read(
14181426
tcx,
14191427
&self.machine,
@@ -1438,16 +1446,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
14381446
let provenance = src_alloc
14391447
.provenance()
14401448
.prepare_copy(src_range, dest_offset, num_copies, self)
1441-
.map_err(|e| e.to_interp_error(dest_alloc_id))?;
1449+
.map_err(|e| e.to_interp_error(src_alloc_id))?;
14421450
// Prepare a copy of the initialization mask.
14431451
let init = src_alloc.init_mask().prepare_copy(src_range);
14441452

1445-
// Destination alloc preparations and access hooks.
1446-
let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
1453+
// Destination alloc preparations...
1454+
let (dest_alloc, machine) = self.get_alloc_raw_mut(dest_alloc_id)?;
14471455
let dest_range = alloc_range(dest_offset, size * num_copies);
1456+
// ...and access hooks.
1457+
M::before_alloc_access(tcx, machine, dest_alloc_id)?;
14481458
M::before_memory_write(
14491459
tcx,
1450-
extra,
1460+
machine,
14511461
&mut dest_alloc.extra,
14521462
dest,
14531463
(dest_alloc_id, dest_prov),
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//! Ensure that writing to `S` while initializing `S` errors.
2+
//! Regression test for <https://github.com/rust-lang/rust/issues/142404>.
3+
#![allow(dead_code)]
4+
5+
struct Foo {
6+
x: i32,
7+
y: (),
8+
}
9+
10+
static S: Foo = Foo {
11+
x: 0,
12+
y: unsafe {
13+
(&raw const S.x).cast_mut().write(1); //~ERROR access itself during initialization
14+
},
15+
};
16+
17+
static mut S2: Foo = Foo {
18+
x: 0,
19+
y: unsafe {
20+
S2.x = 1; //~ERROR access itself during initialization
21+
},
22+
};
23+
24+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0080]: encountered static that tried to access itself during initialization
2+
--> $DIR/recursive-static-write.rs:13:9
3+
|
4+
LL | (&raw const S.x).cast_mut().write(1);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `S` failed here
6+
7+
error[E0080]: encountered static that tried to access itself during initialization
8+
--> $DIR/recursive-static-write.rs:20:9
9+
|
10+
LL | S2.x = 1;
11+
| ^^^^^^^^ evaluation of `S2` failed here
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0080`.

tests/ui/consts/recursive-zst-static.default.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
error[E0080]: encountered static that tried to initialize itself with itself
1+
error[E0080]: encountered static that tried to access itself during initialization
22
--> $DIR/recursive-zst-static.rs:10:18
33
|
44
LL | static FOO: () = FOO;
55
| ^^^ evaluation of `FOO` failed here
66

77
error[E0391]: cycle detected when evaluating initializer of static `A`
8-
--> $DIR/recursive-zst-static.rs:13:16
8+
--> $DIR/recursive-zst-static.rs:13:1
99
|
1010
LL | static A: () = B;
11-
| ^
11+
| ^^^^^^^^^^^^
1212
|
1313
note: ...which requires evaluating initializer of static `B`...
14-
--> $DIR/recursive-zst-static.rs:14:16
14+
--> $DIR/recursive-zst-static.rs:14:1
1515
|
1616
LL | static B: () = A;
17-
| ^
17+
| ^^^^^^^^^^^^
1818
= note: ...which again requires evaluating initializer of static `A`, completing the cycle
1919
= note: cycle used when running analysis passes on this crate
2020
= 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

tests/ui/consts/recursive-zst-static.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// See https://github.com/rust-lang/rust/issues/71078 for more details.
99

1010
static FOO: () = FOO;
11-
//~^ ERROR encountered static that tried to initialize itself with itself
11+
//~^ ERROR encountered static that tried to access itself during initialization
1212

1313
static A: () = B; //~ ERROR cycle detected when evaluating initializer of static `A`
1414
static B: () = A;

tests/ui/consts/recursive-zst-static.unleash.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
error[E0080]: encountered static that tried to initialize itself with itself
1+
error[E0080]: encountered static that tried to access itself during initialization
22
--> $DIR/recursive-zst-static.rs:10:18
33
|
44
LL | static FOO: () = FOO;
55
| ^^^ evaluation of `FOO` failed here
66

77
error[E0391]: cycle detected when evaluating initializer of static `A`
8-
--> $DIR/recursive-zst-static.rs:13:16
8+
--> $DIR/recursive-zst-static.rs:13:1
99
|
1010
LL | static A: () = B;
11-
| ^
11+
| ^^^^^^^^^^^^
1212
|
1313
note: ...which requires evaluating initializer of static `B`...
14-
--> $DIR/recursive-zst-static.rs:14:16
14+
--> $DIR/recursive-zst-static.rs:14:1
1515
|
1616
LL | static B: () = A;
17-
| ^
17+
| ^^^^^^^^^^^^
1818
= note: ...which again requires evaluating initializer of static `A`, completing the cycle
1919
= note: cycle used when running analysis passes on this crate
2020
= 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

tests/ui/consts/write-to-static-mut-in-static.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ pub static mut B: () = unsafe { A = 1; };
33
//~^ ERROR modifying a static's initial value
44

55
pub static mut C: u32 = unsafe { C = 1; 0 };
6+
//~^ ERROR static that tried to access itself during initialization
67

78
pub static D: u32 = D;
8-
//~^ ERROR static that tried to initialize itself with itself
9+
//~^ ERROR static that tried to access itself during initialization
910

1011
fn main() {}

tests/ui/consts/write-to-static-mut-in-static.stderr

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ error[E0080]: modifying a static's initial value from another static's initializ
44
LL | pub static mut B: () = unsafe { A = 1; };
55
| ^^^^^ evaluation of `B` failed here
66

7-
error[E0080]: encountered static that tried to initialize itself with itself
8-
--> $DIR/write-to-static-mut-in-static.rs:7:21
7+
error[E0080]: encountered static that tried to access itself during initialization
8+
--> $DIR/write-to-static-mut-in-static.rs:5:34
9+
|
10+
LL | pub static mut C: u32 = unsafe { C = 1; 0 };
11+
| ^^^^^ evaluation of `C` failed here
12+
13+
error[E0080]: encountered static that tried to access itself during initialization
14+
--> $DIR/write-to-static-mut-in-static.rs:8:21
915
|
1016
LL | pub static D: u32 = D;
1117
| ^ evaluation of `D` failed here
1218

13-
error: aborting due to 2 previous errors
19+
error: aborting due to 3 previous errors
1420

1521
For more information about this error, try `rustc --explain E0080`.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
pub static FOO: u32 = FOO;
2-
//~^ ERROR encountered static that tried to initialize itself with itself
2+
//~^ ERROR encountered static that tried to access itself during initialization
33

44
#[derive(Copy, Clone)]
55
pub union Foo {
66
x: u32,
77
}
88

99
pub static BAR: Foo = BAR;
10-
//~^ ERROR encountered static that tried to initialize itself with itself
10+
//~^ ERROR encountered static that tried to access itself during initialization
1111

1212
fn main() {}

tests/ui/recursion/recursive-static-definition.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error[E0080]: encountered static that tried to initialize itself with itself
1+
error[E0080]: encountered static that tried to access itself during initialization
22
--> $DIR/recursive-static-definition.rs:1:23
33
|
44
LL | pub static FOO: u32 = FOO;
55
| ^^^ evaluation of `FOO` failed here
66

7-
error[E0080]: encountered static that tried to initialize itself with itself
7+
error[E0080]: encountered static that tried to access itself during initialization
88
--> $DIR/recursive-static-definition.rs:9:23
99
|
1010
LL | pub static BAR: Foo = BAR;

tests/ui/statics/read_before_init.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
99
use std::mem::MaybeUninit;
1010

11-
pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
12-
//~^ ERROR: encountered static that tried to initialize itself with itself
11+
pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0, 1));
12+
//~^ ERROR: encountered static that tried to access itself during initialization
13+
pub static Y: (i32, MaybeUninit<i32>) = (1, foo(&Y.0, 0));
14+
//~^ ERROR: encountered static that tried to access itself during initialization
1315

14-
const fn foo(x: &i32) -> MaybeUninit<i32> {
16+
const fn foo(x: &i32, num: usize) -> MaybeUninit<i32> {
1517
let mut temp = MaybeUninit::<i32>::uninit();
1618
unsafe {
17-
std::ptr::copy(x, temp.as_mut_ptr(), 1);
19+
std::ptr::copy(x, temp.as_mut_ptr(), num);
1820
}
1921
temp
2022
}
Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
1-
error[E0080]: encountered static that tried to initialize itself with itself
1+
error[E0080]: encountered static that tried to access itself during initialization
22
--> $DIR/read_before_init.rs:11:45
33
|
4-
LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0));
5-
| ^^^^^^^^^ evaluation of `X` failed inside this call
4+
LL | pub static X: (i32, MaybeUninit<i32>) = (1, foo(&X.0, 1));
5+
| ^^^^^^^^^^^^ evaluation of `X` failed inside this call
66
|
77
note: inside `foo`
8-
--> $DIR/read_before_init.rs:17:9
8+
--> $DIR/read_before_init.rs:19:9
99
|
10-
LL | std::ptr::copy(x, temp.as_mut_ptr(), 1);
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
LL | std::ptr::copy(x, temp.as_mut_ptr(), num);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
note: inside `std::ptr::copy::<i32>`
1313
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
1414

15-
error: aborting due to 1 previous error
15+
error[E0080]: encountered static that tried to access itself during initialization
16+
--> $DIR/read_before_init.rs:13:45
17+
|
18+
LL | pub static Y: (i32, MaybeUninit<i32>) = (1, foo(&Y.0, 0));
19+
| ^^^^^^^^^^^^ evaluation of `Y` failed inside this call
20+
|
21+
note: inside `foo`
22+
--> $DIR/read_before_init.rs:19:9
23+
|
24+
LL | std::ptr::copy(x, temp.as_mut_ptr(), num);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
note: inside `std::ptr::copy::<i32>`
27+
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
28+
29+
error: aborting due to 2 previous errors
1630

1731
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)