@@ -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 }
@@ -296,8 +329,9 @@ fn resolve_expr<'tcx>(
296329 // | E& as ...
297330 match expr. kind {
298331 hir:: ExprKind :: AddrOf ( _, _, subexpr) => {
299- // TODO: generalize
300- if let Some ( ExtendedScope ( lifetime) ) = visitor. cx . extended_parent {
332+ // Record an extended lifetime for the operand if needed.
333+ if let Some ( extended_scope) = visitor. cx . extended_parent {
334+ let lifetime = extended_scope. to_scope ( & visitor. scope_tree ) ;
301335 record_subexpr_extended_temp_scopes ( & mut visitor. scope_tree , subexpr, lifetime) ;
302336 }
303337 resolve_expr ( visitor, subexpr, NodeInfo { drop_temps : false , extending : true } ) ;
@@ -563,10 +597,9 @@ fn resolve_local<'tcx>(
563597 // FIXME(super_let): This ignores backward-incompatible drop hints. Implementing BIDs for
564598 // `super let` bindings could improve `tail_expr_drop_order` with regard to `pin!`, etc.
565599
566- // TODO: generalize
567600 visitor. cx . var_parent = match visitor. cx . extended_parent {
568601 // If the extended parent scope was set, use it.
569- Some ( ExtendedScope ( lifetime ) ) => lifetime ,
602+ Some ( extended_parent ) => extended_parent . to_scope ( & visitor . scope_tree ) . temp_lifetime ,
570603 // Otherwise, like a temporaries, bindings are dropped in the enclosing temporary scope.
571604 None => visitor
572605 . cx
@@ -582,7 +615,7 @@ fn resolve_local<'tcx>(
582615 record_subexpr_extended_temp_scopes (
583616 & mut visitor. scope_tree ,
584617 expr,
585- visitor. cx . var_parent ,
618+ TempLifetime { temp_lifetime : visitor. cx . var_parent , backwards_incompatible : None } ,
586619 ) ;
587620 }
588621
@@ -592,7 +625,8 @@ fn resolve_local<'tcx>(
592625 // When visiting the initializer, extend borrows and `super let`s accessible through
593626 // extending subexpressions to live in the current variable scope (or in the case of
594627 // statics and consts, for the whole program).
595- visitor. cx . extended_parent = Some ( ExtendedScope ( visitor. cx . var_parent ) ) ;
628+ visitor. cx . extended_parent =
629+ Some ( ExtendedScope :: ThroughDeclaration ( visitor. cx . var_parent ) ) ;
596630 }
597631
598632 // Make sure we visit the initializer first.
@@ -694,7 +728,7 @@ fn resolve_local<'tcx>(
694728fn record_subexpr_extended_temp_scopes (
695729 scope_tree : & mut ScopeTree ,
696730 mut expr : & hir:: Expr < ' _ > ,
697- lifetime : Option < Scope > ,
731+ lifetime : TempLifetime ,
698732) {
699733 debug ! ( ?expr, ?lifetime) ;
700734
@@ -741,6 +775,13 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
741775 // account for the destruction scope representing the scope of
742776 // the destructors that run immediately after it completes.
743777 if node_info. drop_temps {
778+ // If this scope corresponds to an extending subexpression, we can extend certain
779+ // temporaries' scopes through it.
780+ if node_info. extending && self . cx . extended_parent . is_none ( ) {
781+ self . cx . extended_parent = Some ( ExtendedScope :: ThroughExpression (
782+ self . cx . parent . expect ( "extending subexpressions should have parent scopes" ) ,
783+ ) ) ;
784+ }
744785 self . enter_scope ( Scope { local_id : id, data : ScopeData :: Destruction } ) ;
745786 }
746787 self . enter_scope ( Scope { local_id : id, data : ScopeData :: Node } ) ;
0 commit comments