@@ -15,6 +15,7 @@ use rustc_hir::intravisit::{self, Visitor};
1515use rustc_hir:: { Arm , Block , Expr , LetStmt , Pat , PatKind , Stmt } ;
1616use rustc_index:: Idx ;
1717use rustc_middle:: middle:: region:: * ;
18+ use rustc_middle:: thir:: TempLifetime ;
1819use rustc_middle:: ty:: TyCtxt ;
1920use rustc_session:: lint;
2021use rustc_span:: source_map;
@@ -29,7 +30,8 @@ struct Context {
2930 parent : Option < Scope > ,
3031
3132 /// Scope of lifetime-extended temporaries. If `None`, extendable expressions have their usual
32- /// temporary scopes.
33+ /// temporary scopes. Distinguishing the case of non-extended temporaries helps minimize the
34+ /// number of extended lifetimes we record in the [`ScopeTree`]'s `rvalue_scopes` map.
3335 extended_parent : Option < ExtendedScope > ,
3436}
3537
@@ -47,9 +49,32 @@ struct NodeInfo {
4749 extending : bool ,
4850}
4951
50- /// Scope of lifetime-extended temporaries. If the field is `None`, no drop is scheduled for them.
52+ /// Scope of lifetime-extended temporaries.
5153#[ derive( Debug , Copy , Clone ) ]
52- struct ExtendedScope ( Option < Scope > ) ;
54+ enum ExtendedScope {
55+ /// Extendable temporaries' scopes will be extended to match the scope of a `let` statement's
56+ /// bindings, a `const`/`static` item, or a `const` block result. In the case of temporaries
57+ /// extended by `const`s and `static`s, the field is `None`, meaning no drop is scheduled.
58+ ThroughDeclaration ( Option < Scope > ) ,
59+ /// Extendable temporaries will be dropped in the temporary scope enclosing the given scope.
60+ /// This is a separate variant to minimize calls to [`ScopeTree::default_temporary_scope`].
61+ ThroughExpression ( Scope ) ,
62+ }
63+
64+ impl ExtendedScope {
65+ fn to_scope ( self , scope_tree : & ScopeTree ) -> TempLifetime {
66+ match self {
67+ ExtendedScope :: ThroughDeclaration ( temp_lifetime) => {
68+ TempLifetime { temp_lifetime, backwards_incompatible : None }
69+ }
70+ ExtendedScope :: ThroughExpression ( non_extending_parent) => {
71+ let ( temp_scope, backwards_incompatible) =
72+ scope_tree. default_temporary_scope ( non_extending_parent) ;
73+ TempLifetime { temp_lifetime : Some ( temp_scope) , backwards_incompatible }
74+ }
75+ }
76+ }
77+ }
5378
5479struct ScopeResolutionVisitor < ' tcx > {
5580 tcx : TyCtxt < ' tcx > ,
@@ -178,6 +203,14 @@ fn resolve_block<'tcx>(
178203 . scope_tree
179204 . backwards_incompatible_scope
180205 . insert ( local_id, Scope { local_id, data : ScopeData :: Node } ) ;
206+
207+ // To avoid false positives in `tail_expr_drop_order`, make sure extendable
208+ // temporaries are extended past the block tail even if that doesn't change their
209+ // scopes in the current edition.
210+ if visitor. cx . extended_parent . is_none ( ) {
211+ visitor. cx . extended_parent =
212+ Some ( ExtendedScope :: ThroughExpression ( visitor. cx . parent . unwrap ( ) ) ) ;
213+ }
181214 }
182215 resolve_expr ( visitor, tail_expr, NodeInfo { drop_temps, extending : true } ) ;
183216 }
@@ -294,8 +327,9 @@ fn resolve_expr<'tcx>(
294327 // | E& as ...
295328 match expr. kind {
296329 hir:: ExprKind :: AddrOf ( _, _, subexpr) => {
297- // TODO: generalize
298- if let Some ( ExtendedScope ( lifetime) ) = visitor. cx . extended_parent {
330+ // Record an extended lifetime for the operand if needed.
331+ if let Some ( extended_scope) = visitor. cx . extended_parent {
332+ let lifetime = extended_scope. to_scope ( & visitor. scope_tree ) ;
299333 record_subexpr_rvalue_scopes ( & mut visitor. scope_tree , subexpr, lifetime) ;
300334 }
301335 resolve_expr ( visitor, subexpr, NodeInfo { drop_temps : false , extending : true } ) ;
@@ -561,10 +595,9 @@ fn resolve_local<'tcx>(
561595 // FIXME(super_let): This ignores backward-incompatible drop hints. Implementing BIDs for
562596 // `super let` bindings could improve `tail_expr_drop_order` with regard to `pin!`, etc.
563597
564- // TODO: generalize
565598 visitor. cx . var_parent = match visitor. cx . extended_parent {
566599 // If the extended parent scope was set, use it.
567- Some ( ExtendedScope ( lifetime ) ) => lifetime ,
600+ Some ( extended_parent ) => extended_parent . to_scope ( & visitor . scope_tree ) . temp_lifetime ,
568601 // Otherwise, like a temporaries, bindings are dropped in the enclosing temporary scope.
569602 None => visitor
570603 . cx
@@ -577,7 +610,11 @@ fn resolve_local<'tcx>(
577610 if let Some ( pat) = pat
578611 && is_binding_pat ( pat)
579612 {
580- record_subexpr_rvalue_scopes ( & mut visitor. scope_tree , expr, visitor. cx . var_parent ) ;
613+ record_subexpr_rvalue_scopes (
614+ & mut visitor. scope_tree ,
615+ expr,
616+ TempLifetime { temp_lifetime : visitor. cx . var_parent , backwards_incompatible : None } ,
617+ ) ;
581618 }
582619
583620 let prev_extended_parent = visitor. cx . extended_parent ;
@@ -586,7 +623,8 @@ fn resolve_local<'tcx>(
586623 // When visiting the initializer, extend borrows and `super let`s accessible through
587624 // extending subexpressions to live in the current variable scope (or in the case of
588625 // statics and consts, for the whole program).
589- visitor. cx . extended_parent = Some ( ExtendedScope ( visitor. cx . var_parent ) ) ;
626+ visitor. cx . extended_parent =
627+ Some ( ExtendedScope :: ThroughDeclaration ( visitor. cx . var_parent ) ) ;
590628 }
591629
592630 // Make sure we visit the initializer first.
@@ -688,7 +726,7 @@ fn resolve_local<'tcx>(
688726fn record_subexpr_rvalue_scopes (
689727 scope_tree : & mut ScopeTree ,
690728 mut expr : & hir:: Expr < ' _ > ,
691- lifetime : Option < Scope > ,
729+ lifetime : TempLifetime ,
692730) {
693731 debug ! ( ?expr, ?lifetime) ;
694732
@@ -735,6 +773,13 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
735773 // account for the destruction scope representing the scope of
736774 // the destructors that run immediately after it completes.
737775 if node_info. drop_temps {
776+ // If this scope corresponds to an extending subexpression, we can extend certain
777+ // temporaries' scopes through it.
778+ if node_info. extending && self . cx . extended_parent . is_none ( ) {
779+ self . cx . extended_parent = Some ( ExtendedScope :: ThroughExpression (
780+ self . cx . parent . expect ( "extending subexpressions should have parent scopes" ) ,
781+ ) ) ;
782+ }
738783 self . enter_scope ( Scope { local_id : id, data : ScopeData :: Destruction } ) ;
739784 }
740785 self . enter_scope ( Scope { local_id : id, data : ScopeData :: Node } ) ;
0 commit comments