Skip to content

Commit 8e4c70f

Browse files
authored
Rollup merge of #148746 - RalfJung:mutable-ref-in-const, r=davidtwco
const validation: remove check for mutable refs in final value of const This check rejects code that is not necessarily UB, e.g. a mutable ref to a `static mut` that is very carefully used correctly. That led to us having to describe it in the Reference, which uncovered just how ad-hoc this check is (rust-lang/reference#2074). Even without this check, we still reject things like ```rust const C: &mut i32 = &mut 0; ``` This is rejected by const checking -- the part of the frontend that looks at the source code and says whether it is allowed in const context. In the Reference, this restriction is explained [here](https://doc.rust-lang.org/nightly/reference/const_eval.html#r-const-eval.const-expr.borrows). So, the check during validation is just a safety net. And it is already a safety net with gaping holes since we only check `&mut T`, not `&UnsafeCell<T>`, due to the fact that we promote some immutable values that have `!Freeze` type so `&!Freeze` actually can occur in the final value of a const. So... it may be time for me to acknowledge that the "mutable ref in final value of const" check is a cure that's worth than the disease. Nobody asked for that check, I just added it because I was worried about soundness issues when we allow mutable references in constants. Originally it was much stricter, but I had to slowly relax it to its current form to prevent t from firing on code we intend to allow. In the end there are only 3 tests left that trigger this error, and they are all just constants containing references to mutable statics -- not the safest code in the world, but also not so bad that we have to spend a lot of time devising a core language limitation and associated Reference wording to prevent it from ever happening. So... `@rust-lang/wg-const-eval` `@rust-lang/lang` I propose that we allow code like this ```rust static mut S: i32 = 3; const C2: &'static mut i32 = unsafe { &mut * &raw mut S }; ``` `@theemathas` would be great if you could try to poke a hole into this. ;)
2 parents 0ad2cd3 + 3d7c9bd commit 8e4c70f

File tree

12 files changed

+79
-137
lines changed

12 files changed

+79
-137
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,6 @@ const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid re
473473
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
474474
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
475475
const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`
476-
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
477476
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
478477
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
479478
const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero

compiler/rustc_const_eval/src/errors.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
665665
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
666666
PartialPointer => const_eval_validation_partial_pointer,
667667
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
668-
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
669668
NullFnPtr { .. } => const_eval_validation_null_fn_ptr,
670669
NeverVal => const_eval_validation_never_val,
671670
NonnullPtrMaybeNull { .. } => const_eval_validation_nonnull_ptr_out_of_range,
@@ -824,7 +823,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
824823
err.arg("maybe", maybe);
825824
}
826825
MutableRefToImmutable
827-
| MutableRefInConst
828826
| NonnullPtrMaybeNull
829827
| NeverVal
830828
| UnsafeCellInImmutable

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -639,12 +639,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
639639
// This can actually occur with transmutes.
640640
throw_validation_failure!(self.path, MutableRefToImmutable);
641641
}
642-
// In a const, any kind of mutable reference is not good.
643-
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) {
644-
if ptr_expected_mutbl == Mutability::Mut {
645-
throw_validation_failure!(self.path, MutableRefInConst);
646-
}
647-
}
648642
}
649643
}
650644
// Potentially skip recursive check.

compiler/rustc_middle/src/mir/interpret/error.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,6 @@ pub enum ValidationErrorKind<'tcx> {
496496
},
497497
MutableRefToImmutable,
498498
UnsafeCellInImmutable,
499-
MutableRefInConst,
500499
NullFnPtr {
501500
/// Records whether this pointer is definitely null or just may be null.
502501
maybe: bool,

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,36 @@ const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _
7777
const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
7878
//~^ ERROR mutable borrows of temporaries
7979

80+
// Various cases of dangling references.
81+
fn dangling() {
82+
const fn helper_int2ptr() -> Option<&'static mut i32> { unsafe {
83+
// Undefined behaviour (integer as pointer), who doesn't love tests like this.
84+
Some(&mut *(42 as *mut i32))
85+
} }
86+
const INT2PTR: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference
87+
static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference
88+
89+
const fn helper_dangling() -> Option<&'static mut i32> { unsafe {
90+
// Undefined behaviour (dangling pointer), who doesn't love tests like this.
91+
Some(&mut *(&mut 42 as *mut i32))
92+
} }
93+
const DANGLING: Option<&mut i32> = helper_dangling(); //~ ERROR dangling reference
94+
static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); //~ ERROR dangling reference
95+
96+
}
97+
98+
// Allowed, because there is an explicit static mut.
99+
static mut BUFFER: i32 = 42;
100+
const fn ptr_to_buffer() -> Option<&'static mut i32> { unsafe {
101+
Some(&mut *std::ptr::addr_of_mut!(BUFFER))
102+
} }
103+
const MUT_TO_BUFFER: Option<&mut i32> = ptr_to_buffer();
104+
105+
// These are fine! Just statics pointing to mutable statics, nothing fundamentally wrong with this.
106+
static MUT_STATIC: Option<&mut i32> = ptr_to_buffer();
107+
static mut MUT_ARRAY: &mut [u8] = &mut [42];
108+
static MUTEX: std::sync::Mutex<&mut [u8]> = std::sync::Mutex::new(unsafe { &mut *MUT_ARRAY });
109+
80110
fn main() {
81111
println!("{}", unsafe { *A });
82112
unsafe { *B = 4 } // Bad news

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,51 @@ LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
120120
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
121121
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
122122

123-
error: aborting due to 12 previous errors
123+
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
124+
--> $DIR/mut_ref_in_final.rs:86:5
125+
|
126+
LL | const INT2PTR: Option<&mut i32> = helper_int2ptr();
127+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
128+
|
129+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
130+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
131+
HEX_DUMP
132+
}
133+
134+
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
135+
--> $DIR/mut_ref_in_final.rs:87:5
136+
|
137+
LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr();
138+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
139+
|
140+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
141+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
142+
HEX_DUMP
143+
}
144+
145+
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
146+
--> $DIR/mut_ref_in_final.rs:93:5
147+
|
148+
LL | const DANGLING: Option<&mut i32> = helper_dangling();
149+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
150+
|
151+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
152+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
153+
HEX_DUMP
154+
}
155+
156+
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
157+
--> $DIR/mut_ref_in_final.rs:94:5
158+
|
159+
LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling();
160+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
161+
|
162+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
163+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
164+
HEX_DUMP
165+
}
166+
167+
error: aborting due to 16 previous errors
124168

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

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

Lines changed: 0 additions & 40 deletions
This file was deleted.

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

Lines changed: 0 additions & 58 deletions
This file was deleted.

tests/ui/consts/issue-17718-const-bad-values.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ const C1: &'static mut [usize] = &mut [];
99

1010
static mut S: i32 = 3;
1111
const C2: &'static mut i32 = unsafe { &mut S };
12-
//~^ ERROR: encountered mutable reference
1312

1413
fn main() {}

tests/ui/consts/issue-17718-const-bad-values.stderr

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@ LL | const C1: &'static mut [usize] = &mut [];
88
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
99
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
1010

11-
error[E0080]: constructing invalid value: encountered mutable reference in `const` value
12-
--> $DIR/issue-17718-const-bad-values.rs:11:1
13-
|
14-
LL | const C2: &'static mut i32 = unsafe { &mut S };
15-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
16-
|
17-
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
18-
= note: the raw bytes of the constant (size: $PTR, align: $PTR) {
19-
HEX_DUMP
20-
}
21-
22-
error: aborting due to 2 previous errors
11+
error: aborting due to 1 previous error
2312

24-
Some errors have detailed explanations: E0080, E0764.
25-
For more information about an error, try `rustc --explain E0080`.
13+
For more information about this error, try `rustc --explain E0764`.

0 commit comments

Comments
 (0)