@@ -30,7 +30,7 @@ type QualifResults<'mir, 'tcx, Q> =
30
30
rustc_mir_dataflow:: ResultsCursor < ' mir , ' tcx , FlowSensitiveAnalysis < ' mir , ' mir , ' tcx , Q > > ;
31
31
32
32
#[ derive( Default ) ]
33
- pub struct Qualifs < ' mir , ' tcx > {
33
+ pub ( crate ) struct Qualifs < ' mir , ' tcx > {
34
34
has_mut_interior : Option < QualifResults < ' mir , ' tcx , HasMutInterior > > ,
35
35
needs_drop : Option < QualifResults < ' mir , ' tcx , NeedsDrop > > ,
36
36
needs_non_const_drop : Option < QualifResults < ' mir , ' tcx , NeedsNonConstDrop > > ,
@@ -484,11 +484,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
484
484
485
485
Rvalue :: Ref ( _, BorrowKind :: Shared | BorrowKind :: Fake , place)
486
486
| 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 ) ;
492
491
493
492
// If the place is indirect, this is basically a reborrow. We have a reborrow
494
493
// special case above, but for raw pointers and pointers/references to `static` and
@@ -498,6 +497,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
498
497
// `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
499
498
// it is too much of a breaking change to take back.
500
499
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
+ ) ;
501
511
match self . const_kind ( ) {
502
512
// In a const fn all borrows are transient or point to the places given via
503
513
// references in the arguments (so we already checked them with
@@ -506,7 +516,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
506
516
// NOTE: Once we have heap allocations during CTFE we need to figure out
507
517
// how to prevent `const fn` to create long-lived allocations that point
508
518
// 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
+ }
510
524
_ => {
511
525
// Locals with StorageDead are definitely not part of the final constant value, and
512
526
// it is thus inherently safe to permit such locals to have their
@@ -517,7 +531,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
517
531
// The good news is that interning will detect if any unexpected mutable
518
532
// pointer slips through.
519
533
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
+ }
521
537
} else {
522
538
self . check_op ( ops:: CellBorrow ) ;
523
539
}
0 commit comments