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 (https://github.com/rust-lang/reference/issues/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. ;)
This commit is contained in:
commit
8e4c70f02b
12 changed files with 79 additions and 137 deletions
|
|
@ -473,7 +473,6 @@ const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid re
|
|||
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
|
||||
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
|
||||
const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$expected_dyn_type}`, but encountered `{$vtable_dyn_type}`
|
||||
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
|
||||
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
|
||||
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
|
||||
const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero
|
||||
|
|
|
|||
|
|
@ -665,7 +665,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
|
|||
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
|
||||
PartialPointer => const_eval_validation_partial_pointer,
|
||||
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
|
||||
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
|
||||
NullFnPtr { .. } => const_eval_validation_null_fn_ptr,
|
||||
NeverVal => const_eval_validation_never_val,
|
||||
NonnullPtrMaybeNull { .. } => const_eval_validation_nonnull_ptr_out_of_range,
|
||||
|
|
@ -824,7 +823,6 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
|
|||
err.arg("maybe", maybe);
|
||||
}
|
||||
MutableRefToImmutable
|
||||
| MutableRefInConst
|
||||
| NonnullPtrMaybeNull
|
||||
| NeverVal
|
||||
| UnsafeCellInImmutable
|
||||
|
|
|
|||
|
|
@ -639,12 +639,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
|||
// This can actually occur with transmutes.
|
||||
throw_validation_failure!(self.path, MutableRefToImmutable);
|
||||
}
|
||||
// In a const, any kind of mutable reference is not good.
|
||||
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) {
|
||||
if ptr_expected_mutbl == Mutability::Mut {
|
||||
throw_validation_failure!(self.path, MutableRefInConst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Potentially skip recursive check.
|
||||
|
|
|
|||
|
|
@ -496,7 +496,6 @@ pub enum ValidationErrorKind<'tcx> {
|
|||
},
|
||||
MutableRefToImmutable,
|
||||
UnsafeCellInImmutable,
|
||||
MutableRefInConst,
|
||||
NullFnPtr {
|
||||
/// Records whether this pointer is definitely null or just may be null.
|
||||
maybe: bool,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,36 @@ const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _
|
|||
const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
|
||||
//~^ ERROR mutable borrows of temporaries
|
||||
|
||||
// Various cases of dangling references.
|
||||
fn dangling() {
|
||||
const fn helper_int2ptr() -> Option<&'static mut i32> { unsafe {
|
||||
// Undefined behaviour (integer as pointer), who doesn't love tests like this.
|
||||
Some(&mut *(42 as *mut i32))
|
||||
} }
|
||||
const INT2PTR: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference
|
||||
static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference
|
||||
|
||||
const fn helper_dangling() -> Option<&'static mut i32> { unsafe {
|
||||
// Undefined behaviour (dangling pointer), who doesn't love tests like this.
|
||||
Some(&mut *(&mut 42 as *mut i32))
|
||||
} }
|
||||
const DANGLING: Option<&mut i32> = helper_dangling(); //~ ERROR dangling reference
|
||||
static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); //~ ERROR dangling reference
|
||||
|
||||
}
|
||||
|
||||
// Allowed, because there is an explicit static mut.
|
||||
static mut BUFFER: i32 = 42;
|
||||
const fn ptr_to_buffer() -> Option<&'static mut i32> { unsafe {
|
||||
Some(&mut *std::ptr::addr_of_mut!(BUFFER))
|
||||
} }
|
||||
const MUT_TO_BUFFER: Option<&mut i32> = ptr_to_buffer();
|
||||
|
||||
// These are fine! Just statics pointing to mutable statics, nothing fundamentally wrong with this.
|
||||
static MUT_STATIC: Option<&mut i32> = ptr_to_buffer();
|
||||
static mut MUT_ARRAY: &mut [u8] = &mut [42];
|
||||
static MUTEX: std::sync::Mutex<&mut [u8]> = std::sync::Mutex::new(unsafe { &mut *MUT_ARRAY });
|
||||
|
||||
fn main() {
|
||||
println!("{}", unsafe { *A });
|
||||
unsafe { *B = 4 } // Bad news
|
||||
|
|
|
|||
|
|
@ -120,7 +120,51 @@ LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
|
|||
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
|
||||
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
|
||||
--> $DIR/mut_ref_in_final.rs:86:5
|
||||
|
|
||||
LL | const INT2PTR: Option<&mut i32> = helper_int2ptr();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
|
||||
--> $DIR/mut_ref_in_final.rs:87:5
|
||||
|
|
||||
LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
|
||||
--> $DIR/mut_ref_in_final.rs:93:5
|
||||
|
|
||||
LL | const DANGLING: Option<&mut i32> = helper_dangling();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
|
||||
--> $DIR/mut_ref_in_final.rs:94:5
|
||||
|
|
||||
LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0080, E0716, E0764.
|
||||
For more information about an error, try `rustc --explain E0080`.
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
//@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
|
||||
//@ normalize-stderr: "( 0x[0-9a-f][0-9a-f] │)? ([0-9a-f][0-9a-f] |__ |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> " HEX_DUMP"
|
||||
//@ normalize-stderr: "HEX_DUMP\s*\n\s*HEX_DUMP" -> "HEX_DUMP"
|
||||
//@ dont-require-annotations: NOTE
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
// This file checks that our dynamic checks catch things that the static checks miss.
|
||||
// We do not have static checks for these, because we do not look into function bodies.
|
||||
// We treat all functions as not returning a mutable reference, because there is no way to
|
||||
// do that without causing the borrow checker to complain (see the B4/helper test in
|
||||
// mut_ref_in_final.rs).
|
||||
|
||||
static mut BUFFER: i32 = 42;
|
||||
|
||||
const fn helper() -> Option<&'static mut i32> { unsafe {
|
||||
Some(&mut *std::ptr::addr_of_mut!(BUFFER))
|
||||
} }
|
||||
const MUT: Option<&mut i32> = helper(); //~ ERROR encountered mutable reference
|
||||
|
||||
const fn helper_int2ptr() -> Option<&'static mut i32> { unsafe {
|
||||
// Undefined behaviour (integer as pointer), who doesn't love tests like this.
|
||||
Some(&mut *(42 as *mut i32))
|
||||
} }
|
||||
const INT2PTR: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference
|
||||
static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr(); //~ ERROR encountered a dangling reference
|
||||
|
||||
const fn helper_dangling() -> Option<&'static mut i32> { unsafe {
|
||||
// Undefined behaviour (dangling pointer), who doesn't love tests like this.
|
||||
Some(&mut *(&mut 42 as *mut i32))
|
||||
} }
|
||||
const DANGLING: Option<&mut i32> = helper_dangling(); //~ ERROR dangling reference
|
||||
static DANGLING_STATIC: Option<&mut i32> = helper_dangling(); //~ ERROR dangling reference
|
||||
|
||||
// These are fine! Just statics pointing to mutable statics, nothing fundamentally wrong with this.
|
||||
static MUT_STATIC: Option<&mut i32> = helper();
|
||||
static mut MUT_ARRAY: &mut [u8] = &mut [42];
|
||||
static MUTEX: Mutex<&mut [u8]> = Mutex::new(unsafe { &mut *MUT_ARRAY });
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in `const` value
|
||||
--> $DIR/mut_ref_in_final_dynamic_check.rs:19:1
|
||||
|
|
||||
LL | const MUT: Option<&mut i32> = helper();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
|
||||
--> $DIR/mut_ref_in_final_dynamic_check.rs:25:1
|
||||
|
|
||||
LL | const INT2PTR: Option<&mut i32> = helper_int2ptr();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
|
||||
--> $DIR/mut_ref_in_final_dynamic_check.rs:26:1
|
||||
|
|
||||
LL | static INT2PTR_STATIC: Option<&mut i32> = helper_int2ptr();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
|
||||
--> $DIR/mut_ref_in_final_dynamic_check.rs:32:1
|
||||
|
|
||||
LL | const DANGLING: Option<&mut i32> = helper_dangling();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (use-after-free)
|
||||
--> $DIR/mut_ref_in_final_dynamic_check.rs:33:1
|
||||
|
|
||||
LL | static DANGLING_STATIC: Option<&mut i32> = helper_dangling();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
||||
|
|
@ -9,6 +9,5 @@ const C1: &'static mut [usize] = &mut [];
|
|||
|
||||
static mut S: i32 = 3;
|
||||
const C2: &'static mut i32 = unsafe { &mut S };
|
||||
//~^ ERROR: encountered mutable reference
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,6 @@ LL | const C1: &'static mut [usize] = &mut [];
|
|||
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
|
||||
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
|
||||
|
||||
error[E0080]: constructing invalid value: encountered mutable reference in `const` value
|
||||
--> $DIR/issue-17718-const-bad-values.rs:11:1
|
||||
|
|
||||
LL | const C2: &'static mut i32 = unsafe { &mut S };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $PTR, align: $PTR) {
|
||||
HEX_DUMP
|
||||
}
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0080, E0764.
|
||||
For more information about an error, try `rustc --explain E0080`.
|
||||
For more information about this error, try `rustc --explain E0764`.
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ static BOO: &mut Foo<()> = &mut Foo(());
|
|||
const BLUNT: &mut i32 = &mut 42;
|
||||
//~^ ERROR: pointing to read-only memory
|
||||
|
||||
// This is fine, it points to a static so there are no questions of pointer identity.
|
||||
const SUBTLE: &mut i32 = unsafe {
|
||||
//~^ ERROR: encountered mutable reference
|
||||
static mut STATIC: i32 = 0;
|
||||
&mut STATIC
|
||||
};
|
||||
|
|
|
|||
|
|
@ -43,17 +43,6 @@ LL | const BLUNT: &mut i32 = &mut 42;
|
|||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value: encountered mutable reference in `const` value
|
||||
--> $DIR/mutable_references.rs:28:1
|
||||
|
|
||||
LL | const SUBTLE: &mut i32 = unsafe {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
||||
|
|
||||
= 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.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: constructing invalid value at .x.<deref>: encountered `UnsafeCell` in read-only memory
|
||||
--> $DIR/mutable_references.rs:40:1
|
||||
|
|
||||
|
|
@ -210,7 +199,7 @@ help: skipping check that does not even have a feature gate
|
|||
LL | const RAW_MUT_COERCE: SyncPtr<i32> = SyncPtr { x: &mut 0 };
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 16 previous errors; 1 warning emitted
|
||||
error: aborting due to 15 previous errors; 1 warning emitted
|
||||
|
||||
Some errors have detailed explanations: E0080, E0594.
|
||||
For more information about an error, try `rustc --explain E0080`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue