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] blocks and `if` expressions. This is used in performing lifetime 
42+ /// extension on block tail expressions: if we've already extended the temporary scopes of 
43+ /// extending borrows within a block's tail when checking a parent `let` statement or block, we 
44+ /// don't want to re-extend 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,17 @@ 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 in Rust 2024. 
176+             if  !visitor. extended_blocks . contains ( & blk. hir_id . local_id )  { 
177+                 let  blk_result_scope = prev_cx. parent . and_then ( |blk_parent| { 
178+                     visitor. scope_tree . default_temporary_scope ( blk_parent) . 0 
179+                 } ) ; 
180+                 record_rvalue_scope_if_borrow_expr ( visitor,  tail_expr,  blk_result_scope) ; 
181+             } 
163182            resolve_expr ( visitor,  tail_expr,  terminating) ; 
164183        } 
165184    } 
@@ -354,6 +373,22 @@ fn resolve_expr<'tcx>(
354373
355374        hir:: ExprKind :: If ( cond,  then,  Some ( otherwise) )  => { 
356375            let  expr_cx = visitor. cx ; 
376+             // If we haven't already checked for temporary lifetime extension due to a parent `let` 
377+             // statement initializer or block, do so. This, e.g., allows `format!("temp")` in 
378+             // `if cond { &format!("temp") } else { "" }` to outlive the block even when the `if` 
379+             // expression itself is not in a `let` statement initializer. The same rules for `let` 
380+             // are used here, so non-extending borrows are unaffected: 
381+             // `if cond { f(&format!("temp")) } else { "" }` 
382+             // drops `format!("temp")` at the end of the block in all editions. 
383+             // This also allows `super let` in the then and else blocks to have the scope of the 
384+             // result of the block, as expected. 
385+             if  !visitor. extended_blocks . contains ( & expr. hir_id . local_id )  { 
386+                 let  blk_result_scope = expr_cx
387+                     . parent 
388+                     . and_then ( |if_parent| visitor. scope_tree . default_temporary_scope ( if_parent) . 0 ) ; 
389+                 record_rvalue_scope_if_borrow_expr ( visitor,  then,  blk_result_scope) ; 
390+                 record_rvalue_scope_if_borrow_expr ( visitor,  otherwise,  blk_result_scope) ; 
391+             } 
357392            let  data = if  expr. span . at_least_rust_2024 ( )  { 
358393                ScopeData :: IfThenRescope 
359394            }  else  { 
@@ -369,6 +404,13 @@ fn resolve_expr<'tcx>(
369404
370405        hir:: ExprKind :: If ( cond,  then,  None )  => { 
371406            let  expr_cx = visitor. cx ; 
407+             // As above, perform lifetime extension on the consequent block. 
408+             if  !visitor. extended_blocks . contains ( & expr. hir_id . local_id )  { 
409+                 let  blk_result_scope = expr_cx
410+                     . parent 
411+                     . and_then ( |if_parent| visitor. scope_tree . default_temporary_scope ( if_parent) . 0 ) ; 
412+                 record_rvalue_scope_if_borrow_expr ( visitor,  then,  blk_result_scope) ; 
413+             } 
372414            let  data = if  expr. span . at_least_rust_2024 ( )  { 
373415                ScopeData :: IfThenRescope 
374416            }  else  { 
@@ -473,7 +515,7 @@ fn resolve_local<'tcx>(
473515            if  let  Some ( scope)  =
474516                visitor. extended_super_lets . remove ( & pat. unwrap ( ) . hir_id . local_id )  =>
475517        { 
476-             // This expression was lifetime-extended by a parent let binding. E.g. 
518+             // This expression was lifetime-extended by a parent let binding or block . E.g. 
477519            // 
478520            //     let a = { 
479521            //         super let b = temp(); 
@@ -489,7 +531,8 @@ fn resolve_local<'tcx>(
489531            true 
490532        } 
491533        LetKind :: Super  => { 
492-             // This `super let` is not subject to lifetime extension from a parent let binding. E.g. 
534+             // This `super let` is not subject to lifetime extension from a parent let binding or 
535+             // block. E.g. 
493536            // 
494537            //     identity({ super let x = temp(); &x }).method(); 
495538            // 
@@ -500,10 +543,16 @@ fn resolve_local<'tcx>(
500543            if  let  Some ( inner_scope)  = visitor. cx . var_parent  { 
501544                ( visitor. cx . var_parent ,  _)  = visitor. scope_tree . default_temporary_scope ( inner_scope) 
502545            } 
503-             // Don't lifetime-extend child `super let`s or block tail expressions' temporaries in 
504-             // the initializer when this `super let` is not itself extended by a parent `let` 
505-             // (#145784). Block tail expressions are temporary drop scopes in Editions 2024 and 
506-             // later, their temps shouldn't outlive the block in e.g. `f(pin!({ &temp() }))`. 
546+             // Don't apply lifetime extension to the initializer of non-extended `super let`. 
547+             // This helps ensure that `{ super let x = &$EXPR; x }` is equivalent to `&$EXPR` in 
548+             // non-extending contexts: we want to avoid extending temporaries in `$EXPR` past what 
549+             // their temporary scopes would otherwise be (#145784). 
550+             // Currently, this shouldn't do anything. The discrepancy in #145784 was due to 
551+             // `{ super let x = &{ &temp() }; x }` extending `temp()` to outlive its immediately 
552+             // enclosing temporary scope (the block tail expression in Rust 2024), whereas in a 
553+             // non-extending context, `&{ &temp() }` would drop `temp()` at the end of the block. 
554+             // This particular quirk no longer exists: lifetime extension rules are applied to block 
555+             // tail expressions, so `temp()` is extended past the block in the latter case as well. 
507556            false 
508557        } 
509558    } ; 
@@ -645,6 +694,9 @@ fn record_rvalue_scope_if_borrow_expr<'tcx>(
645694            record_rvalue_scope_if_borrow_expr ( visitor,  subexpr,  blk_id) 
646695        } 
647696        hir:: ExprKind :: Block ( block,  _)  => { 
697+             // Mark the block as extending, so we know its extending borrows and `super let`s have 
698+             // extended scopes when checking the block itself. 
699+             visitor. extended_blocks . insert ( block. hir_id . local_id ) ; 
648700            if  let  Some ( subexpr)  = block. expr  { 
649701                record_rvalue_scope_if_borrow_expr ( visitor,  subexpr,  blk_id) ; 
650702            } 
@@ -657,6 +709,9 @@ fn record_rvalue_scope_if_borrow_expr<'tcx>(
657709            } 
658710        } 
659711        hir:: ExprKind :: If ( _,  then_block,  else_block)  => { 
712+             // Mark the expression as extending, so we know its extending borrows and `super let`s 
713+             // have extended scopes when checking the `if` expression's blocks. 
714+             visitor. extended_blocks . insert ( expr. hir_id . local_id ) ; 
660715            record_rvalue_scope_if_borrow_expr ( visitor,  then_block,  blk_id) ; 
661716            if  let  Some ( else_block)  = else_block { 
662717                record_rvalue_scope_if_borrow_expr ( visitor,  else_block,  blk_id) ; 
@@ -822,6 +877,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
822877            tcx, 
823878            scope_tree :  ScopeTree :: default ( ) , 
824879            cx :  Context  {  parent :  None ,  var_parent :  None  } , 
880+             extended_blocks :  Default :: default ( ) , 
825881            extended_super_lets :  Default :: default ( ) , 
826882        } ; 
827883
0 commit comments