@@ -20,7 +20,7 @@ use std::mem;
20
20
use std:: ops:: Deref ;
21
21
22
22
use super :: ops:: { self , NonConstOp , Status } ;
23
- use super :: qualifs:: { self , HasMutInterior , NeedsDrop , NeedsNonConstDrop } ;
23
+ use super :: qualifs:: { self , NeedsDrop , NeedsNonConstDrop } ;
24
24
use super :: resolver:: FlowSensitiveAnalysis ;
25
25
use super :: { ConstCx , Qualif } ;
26
26
use crate :: const_eval:: is_unstable_const_fn;
@@ -31,7 +31,6 @@ type QualifResults<'mir, 'tcx, Q> =
31
31
32
32
#[ derive( Default ) ]
33
33
pub ( crate ) struct Qualifs < ' mir , ' tcx > {
34
- has_mut_interior : Option < QualifResults < ' mir , ' tcx , HasMutInterior > > ,
35
34
needs_drop : Option < QualifResults < ' mir , ' tcx , NeedsDrop > > ,
36
35
needs_non_const_drop : Option < QualifResults < ' mir , ' tcx , NeedsNonConstDrop > > ,
37
36
}
@@ -97,36 +96,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
97
96
needs_non_const_drop. get ( ) . contains ( local)
98
97
}
99
98
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
-
130
99
fn in_return_place (
131
100
& mut self ,
132
101
ccx : & ' mir ConstCx < ' mir , ' tcx > ,
@@ -152,7 +121,6 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
152
121
ConstQualifs {
153
122
needs_drop : self . needs_drop ( ccx, RETURN_PLACE , return_loc) ,
154
123
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) ,
156
124
tainted_by_errors,
157
125
}
158
126
}
@@ -373,6 +341,21 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
373
341
}
374
342
}
375
343
}
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
+ }
376
359
}
377
360
378
361
impl < ' tcx > Visitor < ' tcx > for Checker < ' _ , ' tcx > {
@@ -484,20 +467,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
484
467
485
468
Rvalue :: Ref ( _, BorrowKind :: Shared | BorrowKind :: Fake , place)
486
469
| 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 ) ;
492
474
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 {
501
476
match self . const_kind ( ) {
502
477
// In a const fn all borrows are transient or point to the places given via
503
478
// references in the arguments (so we already checked them with
@@ -508,6 +483,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
508
483
// to (interior) mutable memory.
509
484
hir:: ConstContext :: ConstFn => self . check_op ( ops:: TransientCellBorrow ) ,
510
485
_ => {
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.
511
491
// Locals with StorageDead are definitely not part of the final constant value, and
512
492
// it is thus inherently safe to permit such locals to have their
513
493
// 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> {
516
496
// `StorageDead` in every control flow path leading to a `return` terminator.
517
497
// The good news is that interning will detect if any unexpected mutable
518
498
// pointer slips through.
519
- if self . local_has_storage_dead ( place. local ) {
499
+ if place . is_indirect ( ) || self . local_has_storage_dead ( place. local ) {
520
500
self . check_op ( ops:: TransientCellBorrow ) ;
521
501
} else {
522
502
self . check_op ( ops:: CellBorrow ) ;
0 commit comments