Skip to content

Commit 02c0cfe

Browse files
committed
tests and comments related to the tail-expression problem
1 parent 4fde337 commit 02c0cfe

13 files changed

+160
-47
lines changed

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -302,10 +302,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>(
302302
is_static: bool,
303303
) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> {
304304
// `is_static` just means "in static", it could still be a promoted!
305-
debug_assert_eq!(
306-
is_static,
307-
ecx.tcx.static_mutability(cid.instance.def_id()).is_some()
308-
);
305+
debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some());
309306

310307
let res = ecx.load_mir(cid.instance.def, cid.promoted);
311308
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {

compiler/rustc_const_eval/src/interpret/intern.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,14 @@ pub fn intern_const_alloc_recursive<
246246
// that we are starting in, and all other allocations that we are encountering recursively.
247247
let (base_mutability, inner_mutability) = match intern_kind {
248248
InternKind::Constant | InternKind::Promoted => {
249-
// Completely immutable.
249+
// Completely immutable. Interning anything mutably here can only lead to unsoundness,
250+
// since all consts are conceptually independent values but share the same underlying
251+
// memory.
252+
// This means we do intern allocations caused by the "tail expression" / "outer scope"
253+
// rule as immutable. We rely on const-checking ensuring that no mutable memory can
254+
// occur there: mutable references and interior mutability must be rejected even when
255+
// the corresponding feature gates are enabled, so only `&Freeze` references remain, and
256+
// hence interning immutably is sound.
250257
(Mutability::Not, Mutability::Not)
251258
}
252259
InternKind::Static(Mutability::Not) => {
@@ -257,13 +264,14 @@ pub fn intern_const_alloc_recursive<
257264
} else {
258265
Mutability::Mut
259266
},
260-
// Inner allocations are never mutable.
267+
// Inner allocations are never mutable. They can only arise via the "tail
268+
// expression" / "outer scope" rule, and we treat them consistently with `const`.
261269
Mutability::Not,
262270
)
263271
}
264272
InternKind::Static(Mutability::Mut) => {
265273
// Just make everything mutable. We accept code like
266-
// `static mut X = &mut 42`, so even inner allocations need to be mutable.
274+
// `static mut X = &mut [42]`, so even inner allocations need to be mutable.
267275
(Mutability::Mut, Mutability::Mut)
268276
}
269277
};
@@ -326,7 +334,7 @@ pub fn intern_const_alloc_recursive<
326334
while let Some(alloc_id) = todo.pop() {
327335
if let Some((_, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) {
328336
// This still needs to be interned. We keep the old logic around here to avoid changing
329-
// rustc behavior; eventually this should all be removed in favor of ignroing all types
337+
// rustc behavior; eventually this should all be removed in favor of ignoring all types
330338
// and interning everything with a simple `intern_shallow` loop.
331339
match intern_kind {
332340
// Statics may point to mutable allocations.

compiler/rustc_const_eval/src/transform/check_consts/check.rs

+4
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
387387
if self.local_has_storage_dead(local) {
388388
self.check_op(ops::TransientMutBorrow(kind));
389389
} else {
390+
// This check is *very important*. If we let this borrow leak into the final
391+
// value, it would be treated as read-only memory, which would be unsound!
390392
self.check_op(ops::MutBorrow(kind));
391393
}
392394
}
@@ -528,6 +530,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
528530
if self.local_has_storage_dead(place.local) {
529531
self.check_op(ops::TransientCellBorrow);
530532
} else {
533+
// This check is *very important*. If we let this borrow leak into the final
534+
// value, it would be treated as read-only memory, which would be unsound!
531535
self.check_op(ops::CellBorrow);
532536
}
533537
}

tests/ui/consts/const-mut-refs/mut_ref_in_final.rs

+17
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,23 @@ static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
4444
// the enclosing scope rule.
4545
const BAR: NotAMutex<&i32> = NotAMutex(UnsafeCell::new(&42));
4646

47+
struct SyncPtr<T> { x : *const T }
48+
unsafe impl<T> Sync for SyncPtr<T> {}
49+
50+
// These pass the lifetime checks because of the "tail expression" / "outer scope" rule.
51+
// (This relies on `SyncPtr` being a curly brace struct.)
52+
// However, we intern the inner memory as read-only.
53+
// The resulting constant would pass all validation checks, so it is crucial that this gets rejected
54+
// by static const checks!
55+
static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
56+
//~^ ERROR mutable references are not allowed
57+
static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
58+
//~^ ERROR mutable references are not allowed
59+
const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
60+
//~^ ERROR mutable references are not allowed
61+
const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
62+
//~^ ERROR mutable references are not allowed
63+
4764
fn main() {
4865
println!("{}", unsafe { *A });
4966
unsafe { *B = 4 } // Bad news

tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr

+25-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,31 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
5454
| | creates a temporary value which is freed while still in use
5555
| using this value as a static requires that borrow lasts for `'static`
5656

57-
error: aborting due to 6 previous errors
57+
error[E0764]: mutable references are not allowed in the final value of statics
58+
--> $DIR/mut_ref_in_final.rs:55:53
59+
|
60+
LL | static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
61+
| ^^^^^^^
62+
63+
error[E0764]: mutable references are not allowed in the final value of statics
64+
--> $DIR/mut_ref_in_final.rs:57:54
65+
|
66+
LL | static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
67+
| ^^^^^^
68+
69+
error[E0764]: mutable references are not allowed in the final value of constants
70+
--> $DIR/mut_ref_in_final.rs:59:52
71+
|
72+
LL | const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
73+
| ^^^^^^^
74+
75+
error[E0764]: mutable references are not allowed in the final value of constants
76+
--> $DIR/mut_ref_in_final.rs:61:53
77+
|
78+
LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
79+
| ^^^^^^
80+
81+
error: aborting due to 10 previous errors
5882

5983
Some errors have detailed explanations: E0716, E0764.
6084
For more information about an error, try `rustc --explain E0716`.

tests/ui/consts/miri_unleashed/mutable_references_err.32bit.stderr

+14-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ error[E0080]: evaluation of constant value failed
7070
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
7171
| ^^^^^^^^^^^^^ constant accesses static
7272

73+
error: unsupported untyped pointer in constant
74+
--> $DIR/mutable_references_err.rs:57:1
75+
|
76+
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
77+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
78+
|
79+
= note: memory only reachable via raw pointers is not supported
80+
7381
warning: skipping const checks
7482
|
7583
help: skipping check that does not even have a feature gate
@@ -142,7 +150,12 @@ help: skipping check that does not even have a feature gate
142150
|
143151
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
144152
| ^^^^^^^^^^^
153+
help: skipping check that does not even have a feature gate
154+
--> $DIR/mutable_references_err.rs:57:45
155+
|
156+
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
157+
| ^^^^^^^
145158

146-
error: aborting due to 7 previous errors; 1 warning emitted
159+
error: aborting due to 8 previous errors; 1 warning emitted
147160

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

tests/ui/consts/miri_unleashed/mutable_references_err.64bit.stderr

+14-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ error[E0080]: evaluation of constant value failed
7070
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
7171
| ^^^^^^^^^^^^^ constant accesses static
7272

73+
error: unsupported untyped pointer in constant
74+
--> $DIR/mutable_references_err.rs:57:1
75+
|
76+
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
77+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
78+
|
79+
= note: memory only reachable via raw pointers is not supported
80+
7381
warning: skipping const checks
7482
|
7583
help: skipping check that does not even have a feature gate
@@ -142,7 +150,12 @@ help: skipping check that does not even have a feature gate
142150
|
143151
LL | const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
144152
| ^^^^^^^^^^^
153+
help: skipping check that does not even have a feature gate
154+
--> $DIR/mutable_references_err.rs:57:45
155+
|
156+
LL | const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
157+
| ^^^^^^^
145158

146-
error: aborting due to 7 previous errors; 1 warning emitted
159+
error: aborting due to 8 previous errors; 1 warning emitted
147160

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

tests/ui/consts/miri_unleashed/mutable_references_err.rs

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const POINTS_TO_MUTABLE2: &i32 = unsafe { &*MUTABLE_REF };
5555
//~| accesses static
5656

5757
const POINTS_TO_MUTABLE_INNER: *const i32 = &mut 42 as *mut _ as *const _;
58+
//~^ ERROR: unsupported untyped pointer in constant
5859

5960
fn main() {
6061
unsafe {

tests/ui/consts/miri_unleashed/static-no-inner-mut.32bit.stderr

+15-17
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
4242
╾ALLOC3╼ │ ╾──╼
4343
}
4444

45-
error: encountered dangling pointer in final constant
46-
--> $DIR/static-no-inner-mut.rs:28:1
47-
|
48-
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr(&AtomicI32::new(42));
49-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50-
51-
error: encountered dangling pointer in final constant
52-
--> $DIR/static-no-inner-mut.rs:29:1
53-
|
54-
LL | static RAW_MUT: SyncPtr<i32> = SyncPtr(&mut 42 as *mut _ as *const _);
55-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56-
5745
warning: skipping const checks
5846
|
5947
help: skipping check that does not even have a feature gate
@@ -76,12 +64,22 @@ help: skipping check that does not even have a feature gate
7664
|
7765
LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
7866
| ^^^^^^^^
79-
help: skipping check for `const_mut_refs` feature
80-
--> $DIR/static-no-inner-mut.rs:29:40
67+
help: skipping check that does not even have a feature gate
68+
--> $DIR/static-no-inner-mut.rs:33:52
69+
|
70+
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
71+
| ^^^^^^^^^^^^^^^^^^^
72+
help: skipping check that does not even have a feature gate
73+
--> $DIR/static-no-inner-mut.rs:34:51
74+
|
75+
LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
76+
| ^^^^^^^
77+
help: skipping check that does not even have a feature gate
78+
--> $DIR/static-no-inner-mut.rs:35:52
8179
|
82-
LL | static RAW_MUT: SyncPtr<i32> = SyncPtr(&mut 42 as *mut _ as *const _);
83-
| ^^^^^^^
80+
LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
81+
| ^^^^^^
8482

85-
error: aborting due to 6 previous errors; 1 warning emitted
83+
error: aborting due to 4 previous errors; 1 warning emitted
8684

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

tests/ui/consts/miri_unleashed/static-no-inner-mut.64bit.stderr

+15-17
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
4242
╾ALLOC3╼ │ ╾──────╼
4343
}
4444

45-
error: encountered dangling pointer in final constant
46-
--> $DIR/static-no-inner-mut.rs:28:1
47-
|
48-
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr(&AtomicI32::new(42));
49-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50-
51-
error: encountered dangling pointer in final constant
52-
--> $DIR/static-no-inner-mut.rs:29:1
53-
|
54-
LL | static RAW_MUT: SyncPtr<i32> = SyncPtr(&mut 42 as *mut _ as *const _);
55-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56-
5745
warning: skipping const checks
5846
|
5947
help: skipping check that does not even have a feature gate
@@ -76,12 +64,22 @@ help: skipping check that does not even have a feature gate
7664
|
7765
LL | static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}};
7866
| ^^^^^^^^
79-
help: skipping check for `const_mut_refs` feature
80-
--> $DIR/static-no-inner-mut.rs:29:40
67+
help: skipping check that does not even have a feature gate
68+
--> $DIR/static-no-inner-mut.rs:33:52
69+
|
70+
LL | static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
71+
| ^^^^^^^^^^^^^^^^^^^
72+
help: skipping check that does not even have a feature gate
73+
--> $DIR/static-no-inner-mut.rs:34:51
74+
|
75+
LL | static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
76+
| ^^^^^^^
77+
help: skipping check that does not even have a feature gate
78+
--> $DIR/static-no-inner-mut.rs:35:52
8179
|
82-
LL | static RAW_MUT: SyncPtr<i32> = SyncPtr(&mut 42 as *mut _ as *const _);
83-
| ^^^^^^^
80+
LL | static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
81+
| ^^^^^^
8482

85-
error: aborting due to 6 previous errors; 1 warning emitted
83+
error: aborting due to 4 previous errors; 1 warning emitted
8684

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

tests/ui/consts/miri_unleashed/static-no-inner-mut.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// stderr-per-bitwidth
22
// compile-flags: -Zunleash-the-miri-inside-of-you
3-
#![feature(const_refs_to_cell)]
3+
#![feature(const_refs_to_cell, const_mut_refs)]
44
// All "inner" allocations that come with a `static` are interned immutably. This means it is
55
// crucial that we do not accept any form of (interior) mutability there.
66

@@ -24,7 +24,12 @@ static REFMUT2: &mut i32 = {let mut x = 0; &mut {x}}; //~ERROR undefined behavio
2424
struct SyncPtr<T> { x : *const T }
2525
unsafe impl<T> Sync for SyncPtr<T> {}
2626

27-
// This one does not get promoted, and then the reference is simply too short-lived.
27+
// All of these pass the lifetime checks because of the "tail expression" / "outer scope" rule.
28+
// (This relies on `SyncPtr` being a curly brace struct.)
29+
// Then they get interned immutably, which is not great.
30+
// `mut_ref_in_final.rs` and `std/cell.rs` ensure that we don't accept this even with the feature
31+
// fate, but for unleashed Miri there's not really any way we can reject them: it's just
32+
// non-dangling raw pointers.
2833
static RAW_SYNC: SyncPtr<AtomicI32> = SyncPtr { x: &AtomicI32::new(42) };
2934
static RAW_MUT_CAST: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
3035
static RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(const_refs_to_cell)]
2+
3+
use std::cell::*;
4+
5+
struct SyncPtr<T> { x : *const T }
6+
unsafe impl<T> Sync for SyncPtr<T> {}
7+
8+
// These pass the lifetime checks because of the "tail expression" / "outer scope" rule.
9+
// (This relies on `SyncPtr` being a curly brace struct.)
10+
// However, we intern the inner memory as read-only.
11+
// The resulting constant would pass all validation checks, so it is crucial that this gets rejected
12+
// by static const checks!
13+
static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
14+
//~^ ERROR: cannot refer to interior mutable data
15+
const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
16+
//~^ ERROR: cannot refer to interior mutable data
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0492]: statics cannot refer to interior mutable data
2+
--> $DIR/refs-to-cell-in-final.rs:13:54
3+
|
4+
LL | static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
5+
| ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
6+
|
7+
= help: to fix this, the value can be extracted to a separate `static` item and then referenced
8+
9+
error[E0492]: constants cannot refer to interior mutable data
10+
--> $DIR/refs-to-cell-in-final.rs:15:53
11+
|
12+
LL | const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
13+
| ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
14+
15+
error: aborting due to 2 previous errors
16+
17+
For more information about this error, try `rustc --explain E0492`.

0 commit comments

Comments
 (0)