@@ -344,7 +344,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
344
344
visitor. visit_ty ( ty) ;
345
345
}
346
346
347
- fn check_mut_borrow ( & mut self , local : Local , kind : hir:: BorrowKind ) {
347
+ fn check_mut_borrow ( & mut self , place : & Place < ' _ > , kind : hir:: BorrowKind ) {
348
348
match self . const_kind ( ) {
349
349
// In a const fn all borrows are transient or point to the places given via
350
350
// references in the arguments (so we already checked them with
@@ -355,10 +355,19 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
355
355
// to mutable memory.
356
356
hir:: ConstContext :: ConstFn => self . check_op ( ops:: TransientMutBorrow ( kind) ) ,
357
357
_ => {
358
+ // For indirect places, we are not creating a new permanent borrow, it's
359
+ // just as transient as the already existing one. For reborrowing references
360
+ // this is handled at the top of `visit_rvalue`, but for raw pointers we handle it
361
+ // here. Pointers/references to `static mut` also end up here.
358
362
// Locals with StorageDead do not live beyond the evaluation and can
359
363
// thus safely be borrowed without being able to be leaked to the final
360
364
// value of the constant.
361
- if self . local_has_storage_dead ( local) {
365
+ // Note: This is only sound if every local that has a `StorageDead` has a
366
+ // `StorageDead` in every control flow path leading to a `return` terminator.
367
+ // The good news is that interning will detect if any unexpected mutable
368
+ // pointer slips through.
369
+ if place. is_indirect_first_projection ( ) || self . local_has_storage_dead ( place. local )
370
+ {
362
371
self . check_op ( ops:: TransientMutBorrow ( kind) ) ;
363
372
} else {
364
373
self . check_op ( ops:: MutBorrow ( kind) ) ;
@@ -460,7 +469,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
460
469
461
470
if !is_allowed {
462
471
self . check_mut_borrow (
463
- place. local ,
472
+ place,
464
473
if matches ! ( rvalue, Rvalue :: Ref ( ..) ) {
465
474
hir:: BorrowKind :: Ref
466
475
} else {
@@ -478,7 +487,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
478
487
place. as_ref ( ) ,
479
488
) ;
480
489
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`,
492
+ // `place_as_reborrow` does not recognize them as such, so we end up here. This
493
+ // should probably be considered a `TransientCellBorrow` (we consider the equivalent
494
+ // mutable case a `TransientMutBorrow`), but such reborrows got accidentally
495
+ // stabilized already and it is too much of a breaking change to take back.
496
+ if borrowed_place_has_mut_interior && !place. is_indirect_first_projection ( ) {
482
497
match self . const_kind ( ) {
483
498
// In a const fn all borrows are transient or point to the places given via
484
499
// references in the arguments (so we already checked them with
@@ -495,6 +510,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
495
510
// final value.
496
511
// Note: This is only sound if every local that has a `StorageDead` has a
497
512
// `StorageDead` in every control flow path leading to a `return` terminator.
513
+ // The good news is that interning will detect if any unexpected mutable
514
+ // pointer slips through.
498
515
if self . local_has_storage_dead ( place. local ) {
499
516
self . check_op ( ops:: TransientCellBorrow ) ;
500
517
} else {
@@ -619,6 +636,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
619
636
if base_ty. is_unsafe_ptr ( ) {
620
637
if place_ref. projection . is_empty ( ) {
621
638
let decl = & self . body . local_decls [ place_ref. local ] ;
639
+ // FIXME: Why is this only checked for unsafe pointers?
640
+ // That will check `static mut` but not `static`.
641
+ // Crucially it then skips the other checks below!
642
+ // Probably, this can be removed entirely once `MutDeref`
643
+ // and `RawMutPtrDeref` are stable.
622
644
if let LocalInfo :: StaticRef { def_id, .. } = * decl. local_info ( ) {
623
645
let span = decl. source_info . span ;
624
646
self . check_static ( def_id, span) ;
@@ -946,6 +968,12 @@ fn place_as_reborrow<'tcx>(
946
968
) -> Option < PlaceRef < ' tcx > > {
947
969
match place. as_ref ( ) . last_projection ( ) {
948
970
Some ( ( place_base, ProjectionElem :: Deref ) ) => {
971
+ // FIXME: why do statics and raw pointers get excluded here? This makes
972
+ // some code involving mutable pointers unstable, but it is unclear
973
+ // why that code is treated differently from mutable references.
974
+ // Once TransientMutBorrow and TransientCellBorrow are stable,
975
+ // this can probably be cleaned up without any behavioral changes.
976
+
949
977
// A borrow of a `static` also looks like `&(*_1)` in the MIR, but `_1` is a `const`
950
978
// that points to the allocation for the static. Don't treat these as reborrows.
951
979
if body. local_decls [ place_base. local ] . is_ref_to_static ( ) {
0 commit comments