@@ -38,7 +38,20 @@ struct ScopeResolutionVisitor<'tcx> {
3838
3939 cx : Context ,
4040
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 : ScopeCompatibility ,
4255}
4356
4457/// Records the lifetime of a local variable as `cx.var_parent`
@@ -467,37 +480,55 @@ fn resolve_local<'tcx>(
467480 // A, but the inner rvalues `a()` and `b()` have an extended lifetime
468481 // due to rule C.
469482
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 , ScopeCompatibility :: 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 , ScopeCompatibility :: FutureCompatible )
495521 }
496522 }
497- }
523+ } ;
498524
499525 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) ;
501532
502533 if let Some ( pat) = pat {
503534 if is_binding_pat ( pat) {
@@ -506,6 +537,7 @@ fn resolve_local<'tcx>(
506537 RvalueCandidate {
507538 target : expr. hir_id . local_id ,
508539 lifetime : visitor. cx . var_parent ,
540+ compat : ScopeCompatibility :: FutureCompatible ,
509541 } ,
510542 ) ;
511543 }
@@ -607,50 +639,106 @@ fn resolve_local<'tcx>(
607639 fn record_rvalue_scope_if_borrow_expr < ' tcx > (
608640 visitor : & mut ScopeResolutionVisitor < ' tcx > ,
609641 expr : & hir:: Expr < ' _ > ,
610- blk_id : Option < Scope > ,
642+ scope : ExtendedTemporaryScope ,
611643 ) {
612644 match expr. kind {
613645 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 ) ;
615647 visitor. scope_tree . record_rvalue_candidate (
616648 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+ } ,
618654 ) ;
619655 }
620656 hir:: ExprKind :: Struct ( _, fields, _) => {
621657 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 ) ;
623659 }
624660 }
625661 hir:: ExprKind :: Array ( subexprs) | hir:: ExprKind :: Tup ( subexprs) => {
626662 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 ) ;
628664 }
629665 }
630666 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 )
632668 }
633669 hir:: ExprKind :: Block ( block, _) => {
634670 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 : ScopeCompatibility :: 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) ;
636690 }
637691 for stmt in block. stmts {
638692 if let hir:: StmtKind :: Let ( local) = stmt. kind
639693 && let Some ( _) = local. super_
640694 {
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 ) ;
642696 }
643697 }
644698 }
645699 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 : ScopeCompatibility :: 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) ;
647718 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 : ScopeCompatibility :: 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) ;
649737 }
650738 }
651739 hir:: ExprKind :: Match ( _, arms, _) => {
652740 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 ) ;
654742 }
655743 }
656744 hir:: ExprKind :: Call ( func, args) => {
@@ -663,7 +751,7 @@ fn resolve_local<'tcx>(
663751 && let Res :: SelfCtor ( _) | Res :: Def ( DefKind :: Ctor ( _, CtorKind :: Fn ) , _) = path. res
664752 {
665753 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 ) ;
667755 }
668756 }
669757 }
0 commit comments