@@ -320,7 +320,10 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
320
320
self . check_op_spanned ( ops:: StaticAccess , span)
321
321
}
322
322
323
- fn check_mut_borrow ( & mut self , place : & Place < ' _ > , kind : hir:: BorrowKind ) {
323
+ /// Returns whether this place can possibly escape the evaluation of the current const/static
324
+ /// initializer. The check assumes that all already existing pointers and references point to
325
+ /// non-escaping places.
326
+ fn place_may_escape ( & mut self , place : & Place < ' _ > ) -> bool {
324
327
let is_transient = match self . const_kind ( ) {
325
328
// In a const fn all borrows are transient or point to the places given via
326
329
// references in the arguments (so we already checked them with
@@ -341,14 +344,16 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
341
344
// value of the constant.
342
345
// Note: This is only sound if every local that has a `StorageDead` has a
343
346
// `StorageDead` in every control flow path leading to a `return` terminator.
344
- // The good news is that interning will detect if any unexpected mutable
345
- // pointer slips through.
347
+ // If anything slips through, there's no safety net -- safe code can create
348
+ // references to variants of `!Freeze` enums as long as that variant is `Freeze`, so
349
+ // interning can't protect us here. (There *is* a safety net for mutable references
350
+ // though, interning will ICE if we miss something here.)
346
351
place. is_indirect ( ) || self . local_is_transient ( place. local )
347
352
}
348
353
} ;
349
- if !is_transient {
350
- self . check_op ( ops :: EscapingMutBorrow ( kind ) ) ;
351
- }
354
+ // Transient places cannot possibly escape because the place doesn't exist any more at the
355
+ // end of evaluation.
356
+ !is_transient
352
357
}
353
358
}
354
359
@@ -406,15 +411,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
406
411
let is_allowed =
407
412
self . const_kind ( ) == hir:: ConstContext :: Static ( hir:: Mutability :: Mut ) ;
408
413
409
- if !is_allowed {
410
- self . check_mut_borrow (
411
- place,
412
- if matches ! ( rvalue, Rvalue :: Ref ( ..) ) {
413
- hir:: BorrowKind :: Ref
414
- } else {
415
- hir:: BorrowKind :: Raw
416
- } ,
417
- ) ;
414
+ if !is_allowed && self . place_may_escape ( place) {
415
+ self . check_op ( ops:: EscapingMutBorrow ( if matches ! ( rvalue, Rvalue :: Ref ( ..) ) {
416
+ hir:: BorrowKind :: Ref
417
+ } else {
418
+ hir:: BorrowKind :: Raw
419
+ } ) ) ;
418
420
}
419
421
}
420
422
@@ -426,51 +428,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
426
428
place. as_ref ( ) ,
427
429
) ;
428
430
429
- // If the place is indirect, this is basically a reborrow. We have a reborrow
430
- // special case above, but for raw pointers and pointers/references to `static` and
431
- // when the `*` is not the first projection, `place_as_reborrow` does not recognize
432
- // them as such, so we end up here. This should probably be considered a
433
- // `TransientCellBorrow` (we consider the equivalent mutable case a
434
- // `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
435
- // it is too much of a breaking change to take back.
436
- // However, we only want to consider places that are obtained by dereferencing
437
- // a *shared* reference. Mutable references to interior mutable data are stable,
438
- // and we don't want `&*&mut interior_mut` to be accepted.
439
- let is_indirect = place. iter_projections ( ) . any ( |( base, proj) | {
440
- matches ! ( proj, ProjectionElem :: Deref )
441
- && matches ! (
442
- base. ty( self . body, self . tcx) . ty. kind( ) ,
443
- ty:: Ref ( _, _, Mutability :: Not ) | ty:: RawPtr ( _, Mutability :: Not )
444
- )
445
- } ) ;
446
-
447
- if borrowed_place_has_mut_interior && !is_indirect {
448
- match self . const_kind ( ) {
449
- // In a const fn all borrows are transient or point to the places given via
450
- // references in the arguments (so we already checked them with
451
- // TransientCellBorrow/CellBorrow as appropriate).
452
- // The borrow checker guarantees that no new non-transient borrows are created.
453
- // NOTE: Once we have heap allocations during CTFE we need to figure out
454
- // how to prevent `const fn` to create long-lived allocations that point
455
- // to (interior) mutable memory.
456
- hir:: ConstContext :: ConstFn => self . check_op ( ops:: TransientCellBorrow ) ,
457
- _ => {
458
- // Locals with StorageDead are definitely not part of the final constant value, and
459
- // it is thus inherently safe to permit such locals to have their
460
- // address taken as we can't end up with a reference to them in the
461
- // final value.
462
- // Note: This is only sound if every local that has a `StorageDead` has a
463
- // `StorageDead` in every control flow path leading to a `return` terminator.
464
- // If anything slips through, there's no safety net -- safe code can create
465
- // references to variants of `!Freeze` enums as long as that variant is `Freeze`,
466
- // so interning can't protect us here.
467
- if self . local_is_transient ( place. local ) {
468
- self . check_op ( ops:: TransientCellBorrow ) ;
469
- } else {
470
- self . check_op ( ops:: CellBorrow ) ;
471
- }
472
- }
473
- }
431
+ if borrowed_place_has_mut_interior && self . place_may_escape ( place) {
432
+ self . check_op ( ops:: EscapingCellBorrow ) ;
474
433
}
475
434
}
476
435
0 commit comments