Skip to content

Commit 98b111e

Browse files
committed
const-eval: error when initializing a static writes to that static
1 parent e61dd43 commit 98b111e

15 files changed

+146
-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: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,13 @@ 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

68+
/// Set to `true` when the last stack frame gets popped, at which point we have to allow
69+
/// accesses to `static_root_ids` for the final return value copy.
70+
last_frame_popped: bool,
71+
6872
/// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes).
6973
union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
7074
}
@@ -101,6 +105,7 @@ impl<'tcx> CompileTimeMachine<'tcx> {
101105
can_access_mut_global,
102106
check_alignment,
103107
static_root_ids: None,
108+
last_frame_popped: false,
104109
union_data_ranges: FxHashMap::default(),
105110
}
106111
}
@@ -705,24 +710,40 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
705710
interp_ok(())
706711
}
707712

708-
fn before_alloc_read(ecx: &InterpCx<'tcx, Self>, alloc_id: AllocId) -> InterpResult<'tcx> {
713+
fn before_alloc_access(
714+
tcx: TyCtxtAt<'tcx>,
715+
machine: &Self,
716+
alloc_id: AllocId,
717+
) -> InterpResult<'tcx> {
718+
if machine.last_frame_popped {
719+
// Get out of the way for the final copy.
720+
return interp_ok(());
721+
}
709722
// Check if this is the currently evaluated static.
710-
if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
723+
if Some(alloc_id) == machine.static_root_ids.map(|(id, _)| id) {
711724
return Err(ConstEvalErrKind::RecursiveStatic).into();
712725
}
713726
// If this is another static, make sure we fire off the query to detect cycles.
714727
// 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) {
728+
if machine.static_root_ids.is_some() {
729+
if let Some(GlobalAlloc::Static(def_id)) = tcx.try_get_global_alloc(alloc_id) {
730+
if tcx.is_foreign_item(def_id) {
718731
throw_unsup!(ExternStatic(def_id));
719732
}
720-
ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
733+
tcx.eval_static_initializer(def_id)?;
721734
}
722735
}
723736
interp_ok(())
724737
}
725738

739+
fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
740+
// Remember when we are popping the last stack frame.
741+
if ecx.stack().len() == 1 {
742+
ecx.machine.last_frame_popped = true;
743+
}
744+
interp_ok(())
745+
}
746+
726747
fn cached_union_data_range<'e>(
727748
ecx: &'e mut InterpCx<'tcx, Self>,
728749
ty: Ty<'tcx>,

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)