Skip to content

Commit 0702701

Browse files
committed
allow mutable references in const values when they point to no memory
1 parent 0f806a9 commit 0702701

File tree

8 files changed

+93
-84
lines changed

8 files changed

+93
-84
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu
453453
const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
454454
const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
455455
const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
456-
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` or `static`
456+
const_eval_validation_mutable_ref_in_const_or_static = {$front_matter}: encountered mutable reference in a `const` or `static`
457457
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
458458
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
459459
const_eval_validation_null_box = {$front_matter}: encountered a null box

compiler/rustc_const_eval/src/errors.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -603,18 +603,18 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
603603
PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => {
604604
const_eval_validation_box_to_uninhabited
605605
}
606-
PtrToUninhabited { ptr_kind: PointerKind::Ref, .. } => {
606+
PtrToUninhabited { ptr_kind: PointerKind::Ref(_), .. } => {
607607
const_eval_validation_ref_to_uninhabited
608608
}
609609

610610
PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static,
611-
PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_static,
611+
PtrToStatic { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_ref_to_static,
612612

613613
PointerAsInt { .. } => const_eval_validation_pointer_as_int,
614614
PartialPointer => const_eval_validation_partial_pointer,
615615
ConstRefToMutable => const_eval_validation_const_ref_to_mutable,
616616
ConstRefToExtern => const_eval_validation_const_ref_to_extern,
617-
MutableRefInConst => const_eval_validation_mutable_ref_in_const,
617+
MutableRefInConstOrStatic => const_eval_validation_mutable_ref_in_const_or_static,
618618
MutableRefToImmutable => const_eval_validation_mutable_ref_to_immutable,
619619
NullFnPtr => const_eval_validation_null_fn_ptr,
620620
NeverVal => const_eval_validation_never_val,
@@ -630,37 +630,39 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
630630
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => {
631631
const_eval_validation_invalid_box_slice_meta
632632
}
633-
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref } => {
633+
InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref(_) } => {
634634
const_eval_validation_invalid_ref_slice_meta
635635
}
636636

637637
InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => {
638638
const_eval_validation_invalid_box_meta
639639
}
640-
InvalidMetaTooLarge { ptr_kind: PointerKind::Ref } => {
640+
InvalidMetaTooLarge { ptr_kind: PointerKind::Ref(_) } => {
641641
const_eval_validation_invalid_ref_meta
642642
}
643-
UnalignedPtr { ptr_kind: PointerKind::Ref, .. } => const_eval_validation_unaligned_ref,
643+
UnalignedPtr { ptr_kind: PointerKind::Ref(_), .. } => {
644+
const_eval_validation_unaligned_ref
645+
}
644646
UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box,
645647

646648
NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box,
647-
NullPtr { ptr_kind: PointerKind::Ref } => const_eval_validation_null_ref,
649+
NullPtr { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_null_ref,
648650
DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => {
649651
const_eval_validation_dangling_box_no_provenance
650652
}
651-
DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref, .. } => {
653+
DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref(_), .. } => {
652654
const_eval_validation_dangling_ref_no_provenance
653655
}
654656
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => {
655657
const_eval_validation_dangling_box_out_of_bounds
656658
}
657-
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref } => {
659+
DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref(_) } => {
658660
const_eval_validation_dangling_ref_out_of_bounds
659661
}
660662
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => {
661663
const_eval_validation_dangling_box_use_after_free
662664
}
663-
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref } => {
665+
DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref(_) } => {
664666
const_eval_validation_dangling_ref_use_after_free
665667
}
666668
InvalidBool { .. } => const_eval_validation_invalid_bool,
@@ -766,7 +768,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
766768
}
767769
NullPtr { .. }
768770
| PtrToStatic { .. }
769-
| MutableRefInConst
771+
| MutableRefInConstOrStatic
770772
| ConstRefToMutable
771773
| ConstRefToExtern
772774
| MutableRefToImmutable

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -445,22 +445,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
445445
// Determine whether this pointer expects to be pointing to something mutable.
446446
let ptr_expected_mutbl = match ptr_kind {
447447
PointerKind::Box => Mutability::Mut,
448-
PointerKind::Ref => {
449-
let tam = value.layout.ty.builtin_deref(false).unwrap();
450-
// ZST never require mutability. We do not take into account interior mutability
451-
// here since we cannot know if there really is an `UnsafeCell` inside
452-
// `Option<UnsafeCell>` -- so we check that in the recursive descent behind this
453-
// reference.
454-
if size == Size::ZERO { Mutability::Not } else { tam.mutbl }
448+
PointerKind::Ref(mutbl) => {
449+
// We do not take into account interior mutability here since we cannot know if
450+
// there really is an `UnsafeCell` inside `Option<UnsafeCell>` -- so we check
451+
// that in the recursive descent behind this reference (controlled by
452+
// `allow_immutable_unsafe_cell`).
453+
mutbl
455454
}
456455
};
457456
// Proceed recursively even for ZST, no reason to skip them!
458457
// `!` is a ZST and we want to validate it.
459458
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
459+
let mut skip_recursive_check = false;
460460
// Let's see what kind of memory this points to.
461461
// `unwrap` since dangling pointers have already been handled.
462462
let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id).unwrap();
463-
match alloc_kind {
463+
let alloc_actual_mutbl = match alloc_kind {
464464
GlobalAlloc::Static(did) => {
465465
// Special handling for pointers to statics (irrespective of their type).
466466
assert!(!self.ecx.tcx.is_thread_local_static(did));
@@ -474,12 +474,6 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
474474
.no_bound_vars()
475475
.expect("statics should not have generic parameters")
476476
.is_freeze(*self.ecx.tcx, ty::ParamEnv::reveal_all());
477-
// Mutability check.
478-
if ptr_expected_mutbl == Mutability::Mut {
479-
if !is_mut {
480-
throw_validation_failure!(self.path, MutableRefToImmutable);
481-
}
482-
}
483477
// Mode-specific checks
484478
match self.ctfe_mode {
485479
Some(
@@ -494,42 +488,49 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
494488
// trigger cycle errors if we try to compute the value of the other static
495489
// and that static refers back to us (potentially through a promoted).
496490
// This could miss some UB, but that's fine.
497-
return Ok(());
491+
skip_recursive_check = true;
498492
}
499493
Some(CtfeValidationMode::Const { .. }) => {
500-
// For consts on the other hand we have to recursively check;
501-
// pattern matching assumes a valid value. However we better make
502-
// sure this is not mutable.
503-
if is_mut {
504-
throw_validation_failure!(self.path, ConstRefToMutable);
505-
}
506494
// We can't recursively validate `extern static`, so we better reject them.
507495
if self.ecx.tcx.is_foreign_item(did) {
508496
throw_validation_failure!(self.path, ConstRefToExtern);
509497
}
510498
}
511499
None => {}
512500
}
501+
// Return alloc mutability
502+
if is_mut { Mutability::Mut } else { Mutability::Not }
513503
}
514-
GlobalAlloc::Memory(alloc) => {
515-
if alloc.inner().mutability == Mutability::Mut
516-
&& matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
517-
{
518-
throw_validation_failure!(self.path, ConstRefToMutable);
519-
}
520-
if ptr_expected_mutbl == Mutability::Mut
521-
&& alloc.inner().mutability == Mutability::Not
522-
{
523-
throw_validation_failure!(self.path, MutableRefToImmutable);
524-
}
525-
}
504+
GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
526505
GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
527506
// These are immutable, we better don't allow mutable pointers here.
528-
if ptr_expected_mutbl == Mutability::Mut {
529-
throw_validation_failure!(self.path, MutableRefToImmutable);
530-
}
507+
Mutability::Not
508+
}
509+
};
510+
// Mutability check.
511+
// If this allocation has size zero, there is no actual mutability here.
512+
let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id);
513+
if size != Size::ZERO {
514+
if ptr_expected_mutbl == Mutability::Mut
515+
&& alloc_actual_mutbl == Mutability::Not
516+
{
517+
throw_validation_failure!(self.path, MutableRefToImmutable);
518+
}
519+
if ptr_expected_mutbl == Mutability::Mut
520+
&& self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref())
521+
{
522+
throw_validation_failure!(self.path, MutableRefInConstOrStatic);
523+
}
524+
if alloc_actual_mutbl == Mutability::Mut
525+
&& matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. }))
526+
{
527+
throw_validation_failure!(self.path, ConstRefToMutable);
531528
}
532529
}
530+
// Potentially skip recursive check.
531+
if skip_recursive_check {
532+
return Ok(());
533+
}
533534
}
534535
let path = &self.path;
535536
ref_tracking.track(place, || {
@@ -598,16 +599,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
598599
}
599600
Ok(true)
600601
}
601-
ty::Ref(_, ty, mutbl) => {
602-
if self.ctfe_mode.is_some_and(|c| !c.may_contain_mutable_ref())
603-
&& *mutbl == Mutability::Mut
604-
{
605-
let layout = self.ecx.layout_of(*ty)?;
606-
if !layout.is_zst() {
607-
throw_validation_failure!(self.path, MutableRefInConst);
608-
}
609-
}
610-
self.check_safe_pointer(value, PointerKind::Ref)?;
602+
ty::Ref(_, _ty, mutbl) => {
603+
self.check_safe_pointer(value, PointerKind::Ref(*mutbl))?;
611604
Ok(true)
612605
}
613606
ty::FnPtr(_sig) => {

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_macros::HashStable;
1212
use rustc_session::CtfeBacktrace;
1313
use rustc_span::{def_id::DefId, Span, DUMMY_SP};
1414
use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange};
15+
use rustc_type_ir::Mutability;
1516

1617
use std::borrow::Cow;
1718
use std::{any::Any, backtrace::Backtrace, fmt};
@@ -367,15 +368,15 @@ pub enum UndefinedBehaviorInfo<'tcx> {
367368

368369
#[derive(Debug, Clone, Copy)]
369370
pub enum PointerKind {
370-
Ref,
371+
Ref(Mutability),
371372
Box,
372373
}
373374

374375
impl IntoDiagnosticArg for PointerKind {
375376
fn into_diagnostic_arg(self) -> DiagnosticArgValue {
376377
DiagnosticArgValue::Str(
377378
match self {
378-
Self::Ref => "ref",
379+
Self::Ref(_) => "ref",
379380
Self::Box => "box",
380381
}
381382
.into(),
@@ -408,7 +409,7 @@ impl From<PointerKind> for ExpectedKind {
408409
fn from(x: PointerKind) -> ExpectedKind {
409410
match x {
410411
PointerKind::Box => ExpectedKind::Box,
411-
PointerKind::Ref => ExpectedKind::Reference,
412+
PointerKind::Ref(_) => ExpectedKind::Reference,
412413
}
413414
}
414415
}
@@ -419,7 +420,7 @@ pub enum ValidationErrorKind<'tcx> {
419420
PartialPointer,
420421
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
421422
PtrToStatic { ptr_kind: PointerKind },
422-
MutableRefInConst,
423+
MutableRefInConstOrStatic,
423424
ConstRefToMutable,
424425
ConstRefToExtern,
425426
MutableRefToImmutable,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// check-pass
22
#![feature(const_mut_refs)]
33

4+
use std::sync::Mutex;
5+
46
struct Foo {
57
x: usize
68
}
@@ -28,6 +30,10 @@ const fn bazz(foo: &mut Foo) -> usize {
2830
foo.x
2931
}
3032

33+
// Empty slices get promoted so this passes the static checks.
34+
// Make sure it also passes the dynamic checks.
35+
static MUTABLE_REFERENCE_HOLDER: Mutex<&mut [u8]> = Mutex::new(&mut []);
36+
3137
fn main() {
3238
let _: [(); foo().bar()] = [(); 1];
3339
let _: [(); baz(&mut foo())] = [(); 2];

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
error[E0080]: it is undefined behavior to use this value
2-
--> $DIR/mut_ref_in_final_dynamic_check.rs:15:1
2+
--> $DIR/mut_ref_in_final_dynamic_check.rs:16:1
33
|
44
LL | const A: Option<&mut i32> = helper();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const` or `static`
66
|
77
= 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.
88
= note: the raw bytes of the constant (size: 4, align: 4) {
9-
2a 00 00 00 │ *...
9+
╾ALLOC0╼ │ ╾──╼
1010
}
1111

12-
error: encountered dangling pointer in final value of constant
13-
--> $DIR/mut_ref_in_final_dynamic_check.rs:22:1
12+
error[E0080]: it is undefined behavior to use this value
13+
--> $DIR/mut_ref_in_final_dynamic_check.rs:18:1
14+
|
15+
LL | static A_STATIC: Option<&mut i32> = helper();
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const` or `static`
1417
|
15-
LL | const B: Option<&mut i32> = helper2();
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
18+
= 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.
19+
= note: the raw bytes of the constant (size: 4, align: 4) {
20+
╾ALLOC0╼ │ ╾──╼
21+
}
1722

1823
error: aborting due to 2 previous errors
1924

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
error[E0080]: it is undefined behavior to use this value
2-
--> $DIR/mut_ref_in_final_dynamic_check.rs:15:1
2+
--> $DIR/mut_ref_in_final_dynamic_check.rs:16:1
33
|
44
LL | const A: Option<&mut i32> = helper();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const` or `static`
66
|
77
= 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.
88
= note: the raw bytes of the constant (size: 8, align: 8) {
9-
2a 00 00 00 00 00 00 00 │ *.......
9+
╾ALLOC0╼ │ ╾──────╼
1010
}
1111

12-
error: encountered dangling pointer in final value of constant
13-
--> $DIR/mut_ref_in_final_dynamic_check.rs:22:1
12+
error[E0080]: it is undefined behavior to use this value
13+
--> $DIR/mut_ref_in_final_dynamic_check.rs:18:1
14+
|
15+
LL | static A_STATIC: Option<&mut i32> = helper();
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<enum-variant(Some)>.0: encountered mutable reference in a `const` or `static`
1417
|
15-
LL | const B: Option<&mut i32> = helper2();
16-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
18+
= 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.
19+
= note: the raw bytes of the constant (size: 8, align: 8) {
20+
╾ALLOC0╼ │ ╾──────╼
21+
}
1722

1823
error: aborting due to 2 previous errors
1924

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// stderr-per-bitwidth
2-
#![feature(const_mut_refs)]
2+
#![feature(const_mut_refs, const_refs_to_static)]
33
#![feature(raw_ref_op)]
44

55
// This file checks that our dynamic checks catch things that the static checks miss.
@@ -8,17 +8,14 @@
88
// do that without causing the borrow checker to complain (see the B4/helper test in
99
// mut_ref_in_final.rs).
1010

11+
static mut BUFFER: i32 = 42;
12+
1113
const fn helper() -> Option<&'static mut i32> { unsafe {
12-
// Undefined behaviour (integer as pointer), who doesn't love tests like this.
13-
Some(&mut *(42 as *mut i32))
14+
Some(&mut *std::ptr::addr_of_mut!(BUFFER))
1415
} }
1516
const A: Option<&mut i32> = helper(); //~ ERROR it is undefined behavior to use this value
16-
//~^ encountered mutable reference in a `const`
17-
18-
const fn helper2() -> Option<&'static mut i32> { unsafe {
19-
// Undefined behaviour (dangling pointer), who doesn't love tests like this.
20-
Some(&mut *(&mut 42 as *mut i32))
21-
} }
22-
const B: Option<&mut i32> = helper2(); //~ ERROR encountered dangling pointer in final value of constant
17+
//~^ encountered mutable reference
18+
static A_STATIC: Option<&mut i32> = helper(); //~ ERROR it is undefined behavior to use this value
19+
//~^ encountered mutable reference
2320

2421
fn main() {}

0 commit comments

Comments
 (0)