@@ -38,7 +38,20 @@ struct ScopeResolutionVisitor<'tcx> {
38
38
39
39
cx : Context ,
40
40
41
- extended_super_lets : FxHashMap < hir:: ItemLocalId , Option < Scope > > ,
41
+ extended_super_lets : FxHashMap < hir:: ItemLocalId , ExtendedTemporaryScope > ,
42
+ }
43
+
44
+ #[ derive( Copy , Clone ) ]
45
+ struct ExtendedTemporaryScope {
46
+ /// The scope of extended temporaries.
47
+ scope : Option < Scope > ,
48
+ /// Whether this lifetime originated from a regular `let` or a `super let` initializer. In the
49
+ /// latter case, this scope may shorten after #145838 if applied to temporaries within block
50
+ /// tail expressions.
51
+ let_kind : LetKind ,
52
+ /// Whether this scope will shorten after #145838. If this is applied to a temporary value,
53
+ /// we'll emit the `macro_extended_temporary_scopes` lint.
54
+ compat : ExtendedTemporaryScopeCompatibility ,
42
55
}
43
56
44
57
/// Records the lifetime of a local variable as `cx.var_parent`
@@ -467,37 +480,55 @@ fn resolve_local<'tcx>(
467
480
// A, but the inner rvalues `a()` and `b()` have an extended lifetime
468
481
// due to rule C.
469
482
470
- if let_kind == LetKind :: Super {
471
- if let Some ( scope) = visitor. extended_super_lets . remove ( & pat. unwrap ( ) . hir_id . local_id ) {
472
- // This expression was lifetime-extended by a parent let binding. E.g.
473
- //
474
- // let a = {
475
- // super let b = temp();
476
- // &b
477
- // };
478
- //
479
- // (Which needs to behave exactly as: let a = &temp();)
480
- //
481
- // Processing of `let a` will have already decided to extend the lifetime of this
482
- // `super let` to its own var_scope. We use that scope.
483
- visitor. cx . var_parent = scope;
484
- } else {
485
- // This `super let` is not subject to lifetime extension from a parent let binding. E.g.
486
- //
487
- // identity({ super let x = temp(); &x }).method();
488
- //
489
- // (Which needs to behave exactly as: identity(&temp()).method();)
490
- //
491
- // Iterate up to the enclosing destruction scope to find the same scope that will also
492
- // be used for the result of the block itself.
493
- if let Some ( inner_scope) = visitor. cx . var_parent {
494
- ( visitor. cx . var_parent , _) = visitor. scope_tree . default_temporary_scope ( inner_scope)
483
+ let ( source_let_kind, compat) = match let_kind {
484
+ // Normal `let` initializers are unaffected by #145838.
485
+ LetKind :: Regular => {
486
+ ( LetKind :: Regular , ExtendedTemporaryScopeCompatibility :: FutureCompatible )
487
+ }
488
+ LetKind :: Super => {
489
+ if let Some ( scope) = visitor. extended_super_lets . remove ( & pat. unwrap ( ) . hir_id . local_id ) {
490
+ // This expression was lifetime-extended by a parent let binding. E.g.
491
+ //
492
+ // let a = {
493
+ // super let b = temp();
494
+ // &b
495
+ // };
496
+ //
497
+ // (Which needs to behave exactly as: let a = &temp();)
498
+ //
499
+ // Processing of `let a` will have already decided to extend the lifetime of this
500
+ // `super let` to its own var_scope. We use that scope.
501
+ visitor. cx . var_parent = scope. scope ;
502
+ // Inherit compatibility from the original `let` statement. If the original `let`
503
+ // was regular, lifetime extension should apply as normal. If the original `let` was
504
+ // `super`, blocks within the initializer will be affected by #145838.
505
+ ( scope. let_kind , scope. compat )
506
+ } else {
507
+ // This `super let` is not subject to lifetime extension from a parent let binding. E.g.
508
+ //
509
+ // identity({ super let x = temp(); &x }).method();
510
+ //
511
+ // (Which needs to behave exactly as: identity(&temp()).method();)
512
+ //
513
+ // Iterate up to the enclosing destruction scope to find the same scope that will also
514
+ // be used for the result of the block itself.
515
+ if let Some ( inner_scope) = visitor. cx . var_parent {
516
+ ( visitor. cx . var_parent , _) =
517
+ visitor. scope_tree . default_temporary_scope ( inner_scope)
518
+ }
519
+ // Blocks within the initializer will be affected by #145838.
520
+ ( LetKind :: Super , ExtendedTemporaryScopeCompatibility :: FutureCompatible )
495
521
}
496
522
}
497
- }
523
+ } ;
498
524
499
525
if let Some ( expr) = init {
500
- record_rvalue_scope_if_borrow_expr ( visitor, expr, visitor. cx . var_parent ) ;
526
+ let scope = ExtendedTemporaryScope {
527
+ scope : visitor. cx . var_parent ,
528
+ let_kind : source_let_kind,
529
+ compat,
530
+ } ;
531
+ record_rvalue_scope_if_borrow_expr ( visitor, expr, scope) ;
501
532
502
533
if let Some ( pat) = pat {
503
534
if is_binding_pat ( pat) {
@@ -506,6 +537,7 @@ fn resolve_local<'tcx>(
506
537
RvalueCandidate {
507
538
target : expr. hir_id . local_id ,
508
539
lifetime : visitor. cx . var_parent ,
540
+ compat : ExtendedTemporaryScopeCompatibility :: FutureCompatible ,
509
541
} ,
510
542
) ;
511
543
}
@@ -607,50 +639,106 @@ fn resolve_local<'tcx>(
607
639
fn record_rvalue_scope_if_borrow_expr < ' tcx > (
608
640
visitor : & mut ScopeResolutionVisitor < ' tcx > ,
609
641
expr : & hir:: Expr < ' _ > ,
610
- blk_id : Option < Scope > ,
642
+ scope : ExtendedTemporaryScope ,
611
643
) {
612
644
match expr. kind {
613
645
hir:: ExprKind :: AddrOf ( _, _, subexpr) => {
614
- record_rvalue_scope_if_borrow_expr ( visitor, subexpr, blk_id ) ;
646
+ record_rvalue_scope_if_borrow_expr ( visitor, subexpr, scope ) ;
615
647
visitor. scope_tree . record_rvalue_candidate (
616
648
subexpr. hir_id ,
617
- RvalueCandidate { target : subexpr. hir_id . local_id , lifetime : blk_id } ,
649
+ RvalueCandidate {
650
+ target : subexpr. hir_id . local_id ,
651
+ lifetime : scope. scope ,
652
+ compat : scope. compat ,
653
+ } ,
618
654
) ;
619
655
}
620
656
hir:: ExprKind :: Struct ( _, fields, _) => {
621
657
for field in fields {
622
- record_rvalue_scope_if_borrow_expr ( visitor, field. expr , blk_id ) ;
658
+ record_rvalue_scope_if_borrow_expr ( visitor, field. expr , scope ) ;
623
659
}
624
660
}
625
661
hir:: ExprKind :: Array ( subexprs) | hir:: ExprKind :: Tup ( subexprs) => {
626
662
for subexpr in subexprs {
627
- record_rvalue_scope_if_borrow_expr ( visitor, subexpr, blk_id ) ;
663
+ record_rvalue_scope_if_borrow_expr ( visitor, subexpr, scope ) ;
628
664
}
629
665
}
630
666
hir:: ExprKind :: Cast ( subexpr, _) => {
631
- record_rvalue_scope_if_borrow_expr ( visitor, subexpr, blk_id )
667
+ record_rvalue_scope_if_borrow_expr ( visitor, subexpr, scope )
632
668
}
633
669
hir:: ExprKind :: Block ( block, _) => {
634
670
if let Some ( subexpr) = block. expr {
635
- record_rvalue_scope_if_borrow_expr ( visitor, subexpr, blk_id) ;
671
+ let tail_expr_scope =
672
+ if scope. let_kind == LetKind :: Super && block. span . at_least_rust_2024 ( ) {
673
+ // The tail expression will no longer be extending after #145838.
674
+ // Since tail expressions are temporary scopes in Rust 2024, lint on
675
+ // temporaries that acquire this (longer) lifetime.
676
+ ExtendedTemporaryScope {
677
+ compat : ExtendedTemporaryScopeCompatibility :: FutureIncompatible {
678
+ shortens_to : Scope {
679
+ local_id : subexpr. hir_id . local_id ,
680
+ data : ScopeData :: Node ,
681
+ } ,
682
+ } ,
683
+ ..scope
684
+ }
685
+ } else {
686
+ // This is extended by a regular `let`, so it won't be changed.
687
+ scope
688
+ } ;
689
+ record_rvalue_scope_if_borrow_expr ( visitor, subexpr, tail_expr_scope) ;
636
690
}
637
691
for stmt in block. stmts {
638
692
if let hir:: StmtKind :: Let ( local) = stmt. kind
639
693
&& let Some ( _) = local. super_
640
694
{
641
- visitor. extended_super_lets . insert ( local. pat . hir_id . local_id , blk_id ) ;
695
+ visitor. extended_super_lets . insert ( local. pat . hir_id . local_id , scope ) ;
642
696
}
643
697
}
644
698
}
645
699
hir:: ExprKind :: If ( _, then_block, else_block) => {
646
- record_rvalue_scope_if_borrow_expr ( visitor, then_block, blk_id) ;
700
+ let then_scope = if scope. let_kind == LetKind :: Super {
701
+ // The then and else blocks will no longer be extending after #145838.
702
+ // Since `if` blocks are temporary scopes in all editions, lint on temporaries
703
+ // that acquire this (longer) lifetime.
704
+ ExtendedTemporaryScope {
705
+ compat : ExtendedTemporaryScopeCompatibility :: FutureIncompatible {
706
+ shortens_to : Scope {
707
+ local_id : then_block. hir_id . local_id ,
708
+ data : ScopeData :: Node ,
709
+ } ,
710
+ } ,
711
+ ..scope
712
+ }
713
+ } else {
714
+ // This is extended by a regular `let`, so it won't be changed.
715
+ scope
716
+ } ;
717
+ record_rvalue_scope_if_borrow_expr ( visitor, then_block, then_scope) ;
647
718
if let Some ( else_block) = else_block {
648
- record_rvalue_scope_if_borrow_expr ( visitor, else_block, blk_id) ;
719
+ let else_scope = if scope. let_kind == LetKind :: Super {
720
+ // The then and else blocks will no longer be extending after #145838.
721
+ // Since `if` blocks are temporary scopes in all editions, lint on temporaries
722
+ // that acquire this (longer) lifetime.
723
+ ExtendedTemporaryScope {
724
+ compat : ExtendedTemporaryScopeCompatibility :: FutureIncompatible {
725
+ shortens_to : Scope {
726
+ local_id : else_block. hir_id . local_id ,
727
+ data : ScopeData :: Node ,
728
+ } ,
729
+ } ,
730
+ ..scope
731
+ }
732
+ } else {
733
+ // This is extended by a regular `let`, so it won't be changed.
734
+ scope
735
+ } ;
736
+ record_rvalue_scope_if_borrow_expr ( visitor, else_block, else_scope) ;
649
737
}
650
738
}
651
739
hir:: ExprKind :: Match ( _, arms, _) => {
652
740
for arm in arms {
653
- record_rvalue_scope_if_borrow_expr ( visitor, arm. body , blk_id ) ;
741
+ record_rvalue_scope_if_borrow_expr ( visitor, arm. body , scope ) ;
654
742
}
655
743
}
656
744
hir:: ExprKind :: Call ( func, args) => {
@@ -663,7 +751,7 @@ fn resolve_local<'tcx>(
663
751
&& let Res :: SelfCtor ( _) | Res :: Def ( DefKind :: Ctor ( _, CtorKind :: Fn ) , _) = path. res
664
752
{
665
753
for arg in args {
666
- record_rvalue_scope_if_borrow_expr ( visitor, arg, blk_id ) ;
754
+ record_rvalue_scope_if_borrow_expr ( visitor, arg, scope ) ;
667
755
}
668
756
}
669
757
}
0 commit comments