88
99use  std:: mem; 
1010
11- use  rustc_data_structures:: fx:: FxHashMap ; 
11+ use  rustc_data_structures:: fx:: { FxHashMap ,   FxHashSet } ; 
1212use  rustc_hir as  hir; 
1313use  rustc_hir:: def:: { CtorKind ,  DefKind ,  Res } ; 
1414use  rustc_hir:: def_id:: DefId ; 
@@ -38,6 +38,14 @@ struct ScopeResolutionVisitor<'tcx> {
3838
3939    cx :  Context , 
4040
41+     /// Tracks [extending] block expressions. This is used in performing lifetime extension on block 
42+ /// tail expressions: if we've already extended the temporary scopes of extending borrows within 
43+ /// a block's tail when checking a parent `let` statement or block, we don't want to re-extend 
44+ /// them to be shorter when checking the block itself. 
45+ /// 
46+ /// [extending]: https://doc.rust-lang.org/nightly/reference/destructors.html#extending-based-on-expressions 
47+ extended_blocks :  FxHashSet < hir:: ItemLocalId > , 
48+ 
4149    extended_super_lets :  FxHashMap < hir:: ItemLocalId ,  Option < Scope > > , 
4250} 
4351
@@ -160,6 +168,20 @@ fn resolve_block<'tcx>(
160168                    . backwards_incompatible_scope 
161169                    . insert ( local_id,  Scope  {  local_id,  data :  ScopeData :: Node  } ) ; 
162170            } 
171+             // If we haven't already checked for temporary lifetime extension due to a parent `let` 
172+             // statement initializer or block, do so. This, e.g., allows `temp()` in `{ &temp() }` 
173+             // to outlive the block even when the block itself is not in a `let` statement 
174+             // initializer. The same rules for `let` are used here, so non-extending borrows are 
175+             // unaffected: `{ f(&temp()) }` drops `temp()` at the end of the block. 
176+             // NB: This should be checked even if the block is from Rust 2021 or before. Macro 
177+             // expansion can result in nested blocks from different editions, and we always want to 
178+             // propagate the outermost extending lifetime to the innermost extending expressions. 
179+             if  !visitor. extended_blocks . contains ( & blk. hir_id . local_id )  { 
180+                 let  blk_result_scope = prev_cx. parent . and_then ( |blk_parent| { 
181+                     visitor. scope_tree . default_temporary_scope ( blk_parent) . 0 
182+                 } ) ; 
183+                 record_rvalue_scope_if_borrow_expr ( visitor,  tail_expr,  blk_result_scope) ; 
184+             } 
163185            resolve_expr ( visitor,  tail_expr,  terminating) ; 
164186        } 
165187    } 
@@ -470,7 +492,7 @@ fn resolve_local<'tcx>(
470492    let  mut  extend_initializer = true ; 
471493    if  let_kind == LetKind :: Super  { 
472494        if  let  Some ( scope)  = visitor. extended_super_lets . remove ( & pat. unwrap ( ) . hir_id . local_id )  { 
473-             // This expression was lifetime-extended by a parent let binding. E.g. 
495+             // This expression was lifetime-extended by a parent let binding or block . E.g. 
474496            // 
475497            //     let a = { 
476498            //         super let b = temp(); 
@@ -483,7 +505,8 @@ fn resolve_local<'tcx>(
483505            // `super let` to its own var_scope. We use that scope. 
484506            visitor. cx . var_parent  = scope; 
485507        }  else  { 
486-             // This `super let` is not subject to lifetime extension from a parent let binding. E.g. 
508+             // This `super let` is not subject to lifetime extension from a parent let binding or 
509+             // block. E.g. 
487510            // 
488511            //     identity({ super let x = temp(); &x }).method(); 
489512            // 
@@ -494,10 +517,16 @@ fn resolve_local<'tcx>(
494517            if  let  Some ( inner_scope)  = visitor. cx . var_parent  { 
495518                ( visitor. cx . var_parent ,  _)  = visitor. scope_tree . default_temporary_scope ( inner_scope) 
496519            } 
497-             // Don't lifetime-extend child `super let`s or block tail expressions' temporaries in 
498-             // the initializer when this `super let` is not itself extended by a parent `let` 
499-             // (#145784). Block tail expressions are temporary drop scopes in Editions 2024 and 
500-             // later, their temps shouldn't outlive the block in e.g. `f(pin!({ &temp() }))`. 
520+             // Don't apply lifetime extension to the initializer of non-extended `super let`. 
521+             // This helps ensure that `{ super let x = &$EXPR; x }` is equivalent to `&$EXPR` in 
522+             // non-extending contexts: we want to avoid extending temporaries in `$EXPR` past what 
523+             // their temporary scopes would otherwise be (#145784). 
524+             // Currently, this shouldn't do anything. The discrepancy in #145784 was due to 
525+             // `{ super let x = &{ &temp() }; x }` extending `temp()` to outlive its immediately 
526+             // enclosing temporary scope (the block tail expression in Rust 2024), whereas in a 
527+             // non-extending context, `&{ &temp() }` would drop `temp()` at the end of the block. 
528+             // This particular quirk no longer exists: lifetime extension rules are applied to block 
529+             // tail expressions, so `temp()` is extended past the block in the latter case as well. 
501530            extend_initializer = false ; 
502531        } 
503532    } 
@@ -639,6 +668,9 @@ fn record_rvalue_scope_if_borrow_expr<'tcx>(
639668            record_rvalue_scope_if_borrow_expr ( visitor,  subexpr,  blk_id) 
640669        } 
641670        hir:: ExprKind :: Block ( block,  _)  => { 
671+             // Mark the block as extending, so we know its extending borrows and `super let`s 
672+             // have extended scopes when checking the block itself. 
673+             visitor. extended_blocks . insert ( block. hir_id . local_id ) ; 
642674            if  let  Some ( subexpr)  = block. expr  { 
643675                record_rvalue_scope_if_borrow_expr ( visitor,  subexpr,  blk_id) ; 
644676            } 
@@ -816,6 +848,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
816848            tcx, 
817849            scope_tree :  ScopeTree :: default ( ) , 
818850            cx :  Context  {  parent :  None ,  var_parent :  None  } , 
851+             extended_blocks :  Default :: default ( ) , 
819852            extended_super_lets :  Default :: default ( ) , 
820853        } ; 
821854
0 commit comments