@@ -344,7 +344,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
344344 visitor. visit_ty ( ty) ;
345345 }
346346
347- fn check_mut_borrow ( & mut self , local : Local , kind : hir:: BorrowKind ) {
347+ fn check_mut_borrow ( & mut self , place : & Place < ' _ > , kind : hir:: BorrowKind ) {
348348 match self . const_kind ( ) {
349349 // In a const fn all borrows are transient or point to the places given via
350350 // references in the arguments (so we already checked them with
@@ -355,10 +355,19 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
355355 // to mutable memory.
356356 hir:: ConstContext :: ConstFn => self . check_op ( ops:: TransientMutBorrow ( kind) ) ,
357357 _ => {
358+ // For indirect places, we are not creating a new permanent borrow, it's just as
359+ // transient as the already existing one. For reborrowing references this is handled
360+ // at the top of `visit_rvalue`, but for raw pointers we handle it here.
361+ // Pointers/references to `static mut` and cases where the `*` is not the first
362+ // projection also end up here.
358363 // Locals with StorageDead do not live beyond the evaluation and can
359364 // thus safely be borrowed without being able to be leaked to the final
360365 // value of the constant.
361- if self . local_has_storage_dead ( local) {
366+ // Note: This is only sound if every local that has a `StorageDead` has a
367+ // `StorageDead` in every control flow path leading to a `return` terminator.
368+ // The good news is that interning will detect if any unexpected mutable
369+ // pointer slips through.
370+ if place. is_indirect ( ) || self . local_has_storage_dead ( place. local ) {
362371 self . check_op ( ops:: TransientMutBorrow ( kind) ) ;
363372 } else {
364373 self . check_op ( ops:: MutBorrow ( kind) ) ;
@@ -460,7 +469,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
460469
461470 if !is_allowed {
462471 self . check_mut_borrow (
463- place. local ,
472+ place,
464473 if matches ! ( rvalue, Rvalue :: Ref ( ..) ) {
465474 hir:: BorrowKind :: Ref
466475 } else {
@@ -478,7 +487,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
478487 place. as_ref ( ) ,
479488 ) ;
480489
481- if borrowed_place_has_mut_interior {
490+ // If the place is indirect, this is basically a reborrow. We have a reborrow
491+ // special case above, but for raw pointers and pointers/references to `static` and
492+ // when the `*` is not the first projection, `place_as_reborrow` does not recognize
493+ // them as such, so we end up here. This should probably be considered a
494+ // `TransientCellBorrow` (we consider the equivalent mutable case a
495+ // `TransientMutBorrow`), but such reborrows got accidentally stabilized already and
496+ // it is too much of a breaking change to take back.
497+ if borrowed_place_has_mut_interior && !place. is_indirect ( ) {
482498 match self . const_kind ( ) {
483499 // In a const fn all borrows are transient or point to the places given via
484500 // references in the arguments (so we already checked them with
@@ -495,6 +511,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
495511 // final value.
496512 // Note: This is only sound if every local that has a `StorageDead` has a
497513 // `StorageDead` in every control flow path leading to a `return` terminator.
514+ // The good news is that interning will detect if any unexpected mutable
515+ // pointer slips through.
498516 if self . local_has_storage_dead ( place. local ) {
499517 self . check_op ( ops:: TransientCellBorrow ) ;
500518 } else {
@@ -946,6 +964,12 @@ fn place_as_reborrow<'tcx>(
946964) -> Option < PlaceRef < ' tcx > > {
947965 match place. as_ref ( ) . last_projection ( ) {
948966 Some ( ( place_base, ProjectionElem :: Deref ) ) => {
967+ // FIXME: why do statics and raw pointers get excluded here? This makes
968+ // some code involving mutable pointers unstable, but it is unclear
969+ // why that code is treated differently from mutable references.
970+ // Once TransientMutBorrow and TransientCellBorrow are stable,
971+ // this can probably be cleaned up without any behavioral changes.
972+
949973 // A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
950974 // that points to the allocation for the static. Don't treat these as reborrows.
951975 if body. local_decls [ place_base. local ] . is_ref_to_static ( ) {
0 commit comments