@@ -320,7 +320,10 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
320320 self . check_op_spanned ( ops:: StaticAccess , span)
321321 }
322322
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 {
324327 let is_transient = match self . const_kind ( ) {
325328 // In a const fn all borrows are transient or point to the places given via
326329 // references in the arguments (so we already checked them with
@@ -346,9 +349,9 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
346349 place. is_indirect ( ) || self . local_is_transient ( place. local )
347350 }
348351 } ;
349- if !is_transient {
350- self . check_op ( ops :: EscapingMutBorrow ( kind ) ) ;
351- }
352+ // Transient places cannot possibly escape because the place doesn't exist any more at the
353+ // end of evaluation.
354+ !is_transient
352355 }
353356}
354357
@@ -406,15 +409,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
406409 let is_allowed =
407410 self . const_kind ( ) == hir:: ConstContext :: Static ( hir:: Mutability :: Mut ) ;
408411
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- ) ;
412+ if !is_allowed && self . place_may_escape ( place) {
413+ self . check_op ( ops:: EscapingMutBorrow ( if matches ! ( rvalue, Rvalue :: Ref ( ..) ) {
414+ hir:: BorrowKind :: Ref
415+ } else {
416+ hir:: BorrowKind :: Raw
417+ } ) ) ;
418418 }
419419 }
420420
@@ -426,50 +426,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
426426 place. as_ref ( ) ,
427427 ) ;
428428
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- // The good news is that interning will detect if any unexpected mutable
465- // pointer slips through.
466- if self . local_is_transient ( place. local ) {
467- self . check_op ( ops:: TransientCellBorrow ) ;
468- } else {
469- self . check_op ( ops:: CellBorrow ) ;
470- }
471- }
472- }
429+ if borrowed_place_has_mut_interior && self . place_may_escape ( place) {
430+ self . check_op ( ops:: EscapingCellBorrow ) ;
473431 }
474432 }
475433
0 commit comments