Skip to content

Commit 3ad70a1

Browse files
committed
const checking: do not do value-based reasoning for interior mutability
1 parent 6a95896 commit 3ad70a1

File tree

4 files changed

+50
-13
lines changed

4 files changed

+50
-13
lines changed

Diff for: compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -412,10 +412,10 @@ pub fn const_validate_mplace<'mir, 'tcx>(
412412
_ if cid.promoted.is_some() => CtfeValidationMode::Promoted,
413413
Some(mutbl) => CtfeValidationMode::Static { mutbl }, // a `static`
414414
None => {
415-
// In normal `const` (not promoted), the outermost allocation is always only copied,
416-
// so having `UnsafeCell` in there is okay despite them being in immutable memory.
417-
let allow_immutable_unsafe_cell = cid.promoted.is_none() && !inner;
418-
CtfeValidationMode::Const { allow_immutable_unsafe_cell }
415+
// This is a normal `const` (not promoted).
416+
// The outermost allocation is always only copied, so having `UnsafeCell` in there
417+
// is okay despite them being in immutable memory.
418+
CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner }
419419
}
420420
};
421421
ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?;

Diff for: compiler/rustc_const_eval/src/transform/check_consts/check.rs

+24-8
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type QualifResults<'mir, 'tcx, Q> =
3030
rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>;
3131

3232
#[derive(Default)]
33-
pub struct Qualifs<'mir, 'tcx> {
33+
pub(crate) struct Qualifs<'mir, 'tcx> {
3434
has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>,
3535
needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>,
3636
needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>,
@@ -484,11 +484,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
484484

485485
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake, place)
486486
| 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-
);
487+
// We don't do value-based reasoning here, since the rules for interior mutability
488+
// are not finalized yet and they seem likely to not be full value-based in the end.
489+
let borrowed_place_has_mut_interior =
490+
!place.ty(self.body, self.tcx).ty.is_freeze(self.tcx, self.param_env);
492491

493492
// If the place is indirect, this is basically a reborrow. We have a reborrow
494493
// special case above, but for raw pointers and pointers/references to `static` and
@@ -498,6 +497,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
498497
// `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
499498
// it is too much of a breaking change to take back.
500499
if borrowed_place_has_mut_interior && !place.is_indirect() {
500+
// We used to do a value-based check here, so when we changed to a purely
501+
// type-based check we started rejecting code that used to work on stable. So
502+
// for that reason we already stabilize "transient borrows of interior mutable
503+
// borrows where value-based reasoning says that there actually is no interior
504+
// mutability". We don't do anything like that for non-transient borrows since
505+
// those are and will remain hard errors.
506+
let allow_transient_on_stable = !qualifs::in_place::<HasMutInterior, _>(
507+
self.ccx,
508+
&mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
509+
place.as_ref(),
510+
);
501511
match self.const_kind() {
502512
// In a const fn all borrows are transient or point to the places given via
503513
// references in the arguments (so we already checked them with
@@ -506,7 +516,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
506516
// NOTE: Once we have heap allocations during CTFE we need to figure out
507517
// how to prevent `const fn` to create long-lived allocations that point
508518
// to (interior) mutable memory.
509-
hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow),
519+
hir::ConstContext::ConstFn => {
520+
if !allow_transient_on_stable {
521+
self.check_op(ops::TransientCellBorrow)
522+
}
523+
}
510524
_ => {
511525
// Locals with StorageDead are definitely not part of the final constant value, and
512526
// it is thus inherently safe to permit such locals to have their
@@ -517,7 +531,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
517531
// The good news is that interning will detect if any unexpected mutable
518532
// pointer slips through.
519533
if self.local_has_storage_dead(place.local) {
520-
self.check_op(ops::TransientCellBorrow);
534+
if !allow_transient_on_stable {
535+
self.check_op(ops::TransientCellBorrow);
536+
}
521537
} else {
522538
self.check_op(ops::CellBorrow);
523539
}

Diff for: tests/ui/consts/refs-to-cell-in-final.rs

+15
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,19 @@ static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
1515
const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
1616
//~^ ERROR: cannot refer to interior mutable data
1717

18+
// This one does not get promoted because of `Drop`, and then enters interesting codepaths because
19+
// as a value it has no interior mutability, but as a type it does. See
20+
// <https://github.com/rust-lang/rust/issues/121610>. Value-based reasoning for interior mutability
21+
// is questionable (https://github.com/rust-lang/unsafe-code-guidelines/issues/493) so for hnow we
22+
// reject this, i.e., this tests that const-qualif does *not* do value-based reasoning.
23+
pub enum JsValue {
24+
Undefined,
25+
Object(Cell<bool>),
26+
}
27+
impl Drop for JsValue {
28+
fn drop(&mut self) {}
29+
}
30+
const UNDEFINED: &JsValue = &JsValue::Undefined;
31+
//~^ERROR: cannot refer to interior mutable data
32+
1833
fn main() {}

Diff for: tests/ui/consts/refs-to-cell-in-final.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ error[E0492]: constants cannot refer to interior mutable data
1212
LL | const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
1313
| ^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
1414

15-
error: aborting due to 2 previous errors
15+
error[E0492]: constants cannot refer to interior mutable data
16+
--> $DIR/refs-to-cell-in-final.rs:30:29
17+
|
18+
LL | const UNDEFINED: &JsValue = &JsValue::Undefined;
19+
| ^^^^^^^^^^^^^^^^^^^ this borrow of an interior mutable value may end up in the final value
20+
21+
error: aborting due to 3 previous errors
1622

1723
For more information about this error, try `rustc --explain E0492`.

0 commit comments

Comments
 (0)