Skip to content

Commit cb4bf0e

Browse files
committed
Auto merge of #122789 - RalfJung:interior-mut-experiment, r=<try>
experiment: remove value-based reasoning for interior mutability This also stabilizes `const_refs_to_cell` as it's just a crater experiment anyway and that reduces the amount of regressions.
2 parents 94b72d6 + 3660dd9 commit cb4bf0e

File tree

6 files changed

+47
-87
lines changed

6 files changed

+47
-87
lines changed

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

+27-47
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::mem;
2020
use std::ops::Deref;
2121

2222
use super::ops::{self, NonConstOp, Status};
23-
use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop};
23+
use super::qualifs::{self, NeedsDrop, NeedsNonConstDrop};
2424
use super::resolver::FlowSensitiveAnalysis;
2525
use super::{ConstCx, Qualif};
2626
use crate::const_eval::is_unstable_const_fn;
@@ -31,7 +31,6 @@ type QualifResults<'mir, 'tcx, Q> =
3131

3232
#[derive(Default)]
3333
pub(crate) struct Qualifs<'mir, 'tcx> {
34-
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
3534
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
3635
needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
3736
}
@@ -97,36 +96,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
9796
needs_non_const_drop.get().contains(local)
9897
}
9998

100-
/// Returns `true` if `local` is `HasMutInterior` at the given `Location`.
101-
///
102-
/// Only updates the cursor if absolutely necessary.
103-
pub fn has_mut_interior(
104-
&mut self,
105-
ccx: &'mir ConstCx<'mir, 'tcx>,
106-
local: Local,
107-
location: Location,
108-
) -> bool {
109-
let ty = ccx.body.local_decls[local].ty;
110-
// Peeking into opaque types causes cycles if the current function declares said opaque
111-
// type. Thus we avoid short circuiting on the type and instead run the more expensive
112-
// analysis that looks at the actual usage within this function
113-
if !ty.has_opaque_types() && !HasMutInterior::in_any_value_of_ty(ccx, ty) {
114-
return false;
115-
}
116-
117-
let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| {
118-
let ConstCx { tcx, body, .. } = *ccx;
119-
120-
FlowSensitiveAnalysis::new(HasMutInterior, ccx)
121-
.into_engine(tcx, body)
122-
.iterate_to_fixpoint()
123-
.into_results_cursor(body)
124-
});
125-
126-
has_mut_interior.seek_before_primary_effect(location);
127-
has_mut_interior.get().contains(local)
128-
}
129-
13099
fn in_return_place(
131100
&mut self,
132101
ccx: &'mir ConstCx<'mir, 'tcx>,
@@ -152,7 +121,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
152121
ConstQualifs {
153122
needs_drop: self.needs_drop(ccx, RETURN_PLACE, return_loc),
154123
needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc),
155-
has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
156124
tainted_by_errors,
157125
}
158126
}
@@ -373,6 +341,21 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
373341
}
374342
}
375343
}
344+
345+
fn has_interior_mut(&self, ty: Ty<'tcx>) -> bool {
346+
match ty.kind() {
347+
// Empty arrays have no interior mutability no matter their element type.
348+
ty::Array(_elem, count)
349+
if count
350+
.try_eval_target_usize(self.tcx, self.param_env)
351+
.is_some_and(|v| v == 0) =>
352+
{
353+
false
354+
}
355+
// Fallback to checking `Freeze`.
356+
_ => !ty.is_freeze(self.tcx, self.param_env),
357+
}
358+
}
376359
}
377360

378361
impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
@@ -484,20 +467,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
484467

485468
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake, place)
486469
| Rvalue::AddressOf(Mutability::Not, place) => {
487-
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
488-
self.ccx,
489-
&mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
490-
place.as_ref(),
491-
);
470+
// We don't do value-based reasoning here, since the rules for interior mutability
471+
// are not finalized yet and they seem likely to not be full value-based in the end.
472+
let borrowed_place_has_mut_interior =
473+
self.has_interior_mut(place.ty(self.body, self.tcx).ty);
492474

493-
// If the place is indirect, this is basically a reborrow. We have a reborrow
494-
// special case above, but for raw pointers and pointers/references to `static` and
495-
// when the `*` is not the first projection, `place_as_reborrow` does not recognize
496-
// them as such, so we end up here. This should probably be considered a
497-
// `TransientCellBorrow` (we consider the equivalent mutable case a
498-
// `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
499-
// it is too much of a breaking change to take back.
500-
if borrowed_place_has_mut_interior && !place.is_indirect() {
475+
if borrowed_place_has_mut_interior {
501476
match self.const_kind() {
502477
// In a const fn all borrows are transient or point to the places given via
503478
// references in the arguments (so we already checked them with
@@ -508,6 +483,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
508483
// to (interior) mutable memory.
509484
hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow),
510485
_ => {
486+
// For indirect places, we are not creating a new permanent borrow, it's just as
487+
// transient as the already existing one. For reborrowing references this is handled
488+
// at the top of `visit_rvalue`, but for raw pointers we handle it here.
489+
// Pointers/references to `static mut` and cases where the `*` is not the first
490+
// projection also end up here.
511491
// Locals with StorageDead are definitely not part of the final constant value, and
512492
// it is thus inherently safe to permit such locals to have their
513493
// address taken as we can't end up with a reference to them in the
@@ -516,7 +496,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
516496
// `StorageDead` in every control flow path leading to a `return` terminator.
517497
// The good news is that interning will detect if any unexpected mutable
518498
// pointer slips through.
519-
if self.local_has_storage_dead(place.local) {
499+
if place.is_indirect() || self.local_has_storage_dead(place.local) {
520500
self.check_op(ops::TransientCellBorrow);
521501
} else {
522502
self.check_op(ops::CellBorrow);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
415415
pub struct TransientCellBorrow;
416416
impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
417417
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
418-
Status::Unstable(sym::const_refs_to_cell)
418+
Status::Allowed
419419
}
420420
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
421421
ccx.tcx

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

-34
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ pub fn in_any_value_of_ty<'tcx>(
2121
tainted_by_errors: Option<ErrorGuaranteed>,
2222
) -> ConstQualifs {
2323
ConstQualifs {
24-
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
2524
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
2625
needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
2726
tainted_by_errors,
@@ -83,39 +82,6 @@ pub trait Qualif {
8382
fn deref_structural<'tcx>(cx: &ConstCx<'_, 'tcx>) -> bool;
8483
}
8584

86-
/// Constant containing interior mutability (`UnsafeCell<T>`).
87-
/// This must be ruled out to make sure that evaluating the constant at compile-time
88-
/// and at *any point* during the run-time would produce the same result. In particular,
89-
/// promotion of temporaries must not change program behavior; if the promoted could be
90-
/// written to, that would be a problem.
91-
pub struct HasMutInterior;
92-
93-
impl Qualif for HasMutInterior {
94-
const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
95-
96-
fn in_qualifs(qualifs: &ConstQualifs) -> bool {
97-
qualifs.has_mut_interior
98-
}
99-
100-
fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
101-
!ty.is_freeze(cx.tcx, cx.param_env)
102-
}
103-
104-
fn in_adt_inherently<'tcx>(
105-
_cx: &ConstCx<'_, 'tcx>,
106-
adt: AdtDef<'tcx>,
107-
_: GenericArgsRef<'tcx>,
108-
) -> bool {
109-
// Exactly one type, `UnsafeCell`, has the `HasMutInterior` qualif inherently.
110-
// It arises structurally for all other types.
111-
adt.is_unsafe_cell()
112-
}
113-
114-
fn deref_structural<'tcx>(_cx: &ConstCx<'_, 'tcx>) -> bool {
115-
false
116-
}
117-
}
118-
11985
/// Constant containing an ADT that implements `Drop`.
12086
/// This must be ruled out because implicit promotion would remove side-effects
12187
/// that occur as part of dropping that value. N.B., the implicit promotion has

compiler/rustc_middle/src/mir/query.rs

-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ pub struct BorrowCheckResult<'tcx> {
196196
/// `Qualif`.
197197
#[derive(Clone, Copy, Debug, Default, TyEncodable, TyDecodable, HashStable)]
198198
pub struct ConstQualifs {
199-
pub has_mut_interior: bool,
200199
pub needs_drop: bool,
201200
pub needs_non_const_drop: bool,
202201
pub tainted_by_errors: Option<ErrorGuaranteed>,

compiler/rustc_mir_transform/src/promote_consts.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,22 @@ impl<'tcx> Validator<'_, 'tcx> {
389389
}
390390

391391
BorrowKind::Shared => {
392-
let has_mut_interior = self.qualif_local::<qualifs::HasMutInterior>(place.local);
393-
if has_mut_interior {
392+
// Let's just see what happens if we reject anything `!Freeze`...
393+
// (Except ZST which definitely can't have interior mut)
394+
let ty = place.ty(self.body, self.tcx).ty;
395+
let has_interior_mut = match ty.kind() {
396+
// Empty arrays have no interior mutability no matter their element type.
397+
ty::Array(_elem, count)
398+
if count
399+
.try_eval_target_usize(self.tcx, self.param_env)
400+
.is_some_and(|v| v == 0) =>
401+
{
402+
false
403+
}
404+
// Fallback to checking `Freeze`.
405+
_ => !ty.is_freeze(self.tcx, self.param_env),
406+
};
407+
if has_interior_mut {
394408
return Err(Unpromotable);
395409
}
396410
}

compiler/rustc_pattern_analysis/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![allow(rustc::untranslatable_diagnostic)]
44
#![allow(rustc::diagnostic_outside_of_impl)]
5+
#![feature(freeze)]
56

67
pub mod constructor;
78
#[cfg(feature = "rustc")]
@@ -90,9 +91,9 @@ pub trait PatCx: Sized + fmt::Debug {
9091
/// Errors that can abort analysis.
9192
type Error: fmt::Debug;
9293
/// The index of an enum variant.
93-
type VariantIdx: Clone + index::Idx + fmt::Debug;
94+
type VariantIdx: Clone + index::Idx + fmt::Debug + std::marker::Freeze;
9495
/// A string literal
95-
type StrLit: Clone + PartialEq + fmt::Debug;
96+
type StrLit: Clone + PartialEq + fmt::Debug + std::marker::Freeze;
9697
/// Extra data to store in a match arm.
9798
type ArmData: Copy + Clone + fmt::Debug;
9899
/// Extra data to store in a pattern.

0 commit comments

Comments
 (0)