Skip to content

Commit 0c92146

Browse files
Rollup merge of rust-lang#121179 - RalfJung:zst-mutable-refs, r=oli-obk
allow mutable references in const values when they point to no memory Fixes rust-lang#120450 The second commit is just some drive-by test suite cleanup. r? `@oli-obk`
2 parents f60576b + d3fc69a commit 0c92146

21 files changed

+209
-387
lines changed

Diff for: compiler/rustc_const_eval/messages.ftl

+1-1
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

Diff for: compiler/rustc_const_eval/src/errors.rs

+13-11
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

Diff for: compiler/rustc_const_eval/src/interpret/validity.rs

+39-46
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) => {

Diff for: compiler/rustc_middle/src/mir/interpret/error.rs

+5-4
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,

Diff for: tests/ui/consts/const-eval/validate_uninhabited_zsts.64bit.stderr

-52
This file was deleted.

Diff for: tests/ui/consts/const-eval/validate_uninhabited_zsts.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// stderr-per-bitwidth
2-
31
const fn foo() -> ! {
42
unsafe { std::mem::transmute(()) }
53
//~^ ERROR evaluation of constant value failed

Diff for: tests/ui/consts/const-eval/validate_uninhabited_zsts.32bit.stderr renamed to tests/ui/consts/const-eval/validate_uninhabited_zsts.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: the type `!` does not permit zero-initialization
2-
--> $DIR/validate_uninhabited_zsts.rs:4:14
2+
--> $DIR/validate_uninhabited_zsts.rs:2:14
33
|
44
LL | unsafe { std::mem::transmute(()) }
55
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
@@ -8,41 +8,41 @@ LL | unsafe { std::mem::transmute(()) }
88
= note: `#[warn(invalid_value)]` on by default
99

1010
error[E0080]: evaluation of constant value failed
11-
--> $DIR/validate_uninhabited_zsts.rs:4:14
11+
--> $DIR/validate_uninhabited_zsts.rs:2:14
1212
|
1313
LL | unsafe { std::mem::transmute(()) }
1414
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a value of the never type `!`
1515
|
1616
note: inside `foo`
17-
--> $DIR/validate_uninhabited_zsts.rs:4:14
17+
--> $DIR/validate_uninhabited_zsts.rs:2:14
1818
|
1919
LL | unsafe { std::mem::transmute(()) }
2020
| ^^^^^^^^^^^^^^^^^^^^^^^
2121
note: inside `FOO`
22-
--> $DIR/validate_uninhabited_zsts.rs:19:33
22+
--> $DIR/validate_uninhabited_zsts.rs:17:33
2323
|
2424
LL | const FOO: [empty::Empty; 3] = [foo(); 3];
2525
| ^^^^^
2626

2727
error[E0080]: evaluation of constant value failed
28-
--> $DIR/validate_uninhabited_zsts.rs:21:42
28+
--> $DIR/validate_uninhabited_zsts.rs:19:42
2929
|
3030
LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
3131
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered a value of uninhabited type `Void`
3232

3333
warning: the type `empty::Empty` does not permit zero-initialization
34-
--> $DIR/validate_uninhabited_zsts.rs:21:42
34+
--> $DIR/validate_uninhabited_zsts.rs:19:42
3535
|
3636
LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
3737
| ^^^^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed
3838
|
3939
note: in this struct field
40-
--> $DIR/validate_uninhabited_zsts.rs:16:22
40+
--> $DIR/validate_uninhabited_zsts.rs:14:22
4141
|
4242
LL | pub struct Empty(Void);
4343
| ^^^^
4444
note: enums with no inhabited variants have no valid value
45-
--> $DIR/validate_uninhabited_zsts.rs:13:5
45+
--> $DIR/validate_uninhabited_zsts.rs:11:5
4646
|
4747
LL | enum Void {}
4848
| ^^^^^^^^^

0 commit comments

Comments
 (0)