@@ -29,7 +29,8 @@ struct Context {
2929 parent : Option < Scope > ,
3030
3131 /// Scope of lifetime-extended temporaries. If `None`, extendable expressions have their usual
32- /// temporary scopes.
32+ /// temporary scopes. Distinguishing the case of non-extended temporaries helps minimize the
33+ /// number of extended lifetimes we record in the [`ScopeTree`]'s `rvalue_scopes` map.
3334 extended_parent : Option < ExtendedScope > ,
3435}
3536
@@ -47,9 +48,32 @@ struct NodeInfo {
4748 extending : bool ,
4849}
4950
50- /// Scope of lifetime-extended temporaries. If the field is `None`, no drop is scheduled for them.
51+ /// Scope of lifetime-extended temporaries.
5152#[ derive( Debug , Copy , Clone ) ]
52- struct ExtendedScope ( Option < Scope > ) ;
53+ enum ExtendedScope {
54+ /// Extendable temporaries' scopes will be extended to match the scope of a `let` statement's
55+ /// bindings, a `const`/`static` item, or a `const` block result. In the case of temporaries
56+ /// extended by `const`s and `static`s, the field is `None`, meaning no drop is scheduled.
57+ ThroughDeclaration ( Option < Scope > ) ,
58+ /// Extendable temporaries will be dropped in the temporary scope enclosing the given scope.
59+ /// This is a separate variant to minimize calls to [`ScopeTree::default_temporary_scope`].
60+ ThroughExpression ( Scope ) ,
61+ }
62+
63+ impl ExtendedScope {
64+ fn to_scope ( self , scope_tree : & ScopeTree ) -> TempLifetime {
65+ match self {
66+ ExtendedScope :: ThroughDeclaration ( temp_lifetime) => {
67+ TempLifetime { temp_lifetime, backwards_incompatible : None }
68+ }
69+ ExtendedScope :: ThroughExpression ( non_extending_parent) => {
70+ let ( temp_scope, backwards_incompatible) =
71+ scope_tree. default_temporary_scope ( non_extending_parent) ;
72+ TempLifetime { temp_lifetime : Some ( temp_scope) , backwards_incompatible }
73+ }
74+ }
75+ }
76+ }
5377
5478struct ScopeResolutionVisitor < ' tcx > {
5579 tcx : TyCtxt < ' tcx > ,
@@ -178,6 +202,14 @@ fn resolve_block<'tcx>(
178202 . scope_tree
179203 . backwards_incompatible_scope
180204 . insert ( local_id, Scope { local_id, data : ScopeData :: Node } ) ;
205+
206+ // To avoid false positives in `tail_expr_drop_order`, make sure extendable
207+ // temporaries are extended past the block tail even if that doesn't change their
208+ // scopes in the current edition.
209+ if visitor. cx . extended_parent . is_none ( ) {
210+ visitor. cx . extended_parent =
211+ Some ( ExtendedScope :: ThroughExpression ( visitor. cx . parent . unwrap ( ) ) ) ;
212+ }
181213 }
182214 resolve_expr ( visitor, tail_expr, NodeInfo { drop_temps, extending : true } ) ;
183215 }
@@ -296,8 +328,9 @@ fn resolve_expr<'tcx>(
296328 // | E& as ...
297329 match expr. kind {
298330 hir:: ExprKind :: AddrOf ( _, _, subexpr) => {
299- // TODO: generalize
300- if let Some ( ExtendedScope ( lifetime) ) = visitor. cx . extended_parent {
331+ // Record an extended lifetime for the operand if needed.
332+ if let Some ( extended_scope) = visitor. cx . extended_parent {
333+ let lifetime = extended_scope. to_scope ( & visitor. scope_tree ) ;
301334 record_subexpr_extended_temp_scopes ( & mut visitor. scope_tree , subexpr, lifetime) ;
302335 }
303336 resolve_expr ( visitor, subexpr, NodeInfo { drop_temps : false , extending : true } ) ;
@@ -563,10 +596,9 @@ fn resolve_local<'tcx>(
563596 // FIXME(super_let): This ignores backward-incompatible drop hints. Implementing BIDs for
564597 // `super let` bindings could improve `tail_expr_drop_order` with regard to `pin!`, etc.
565598
566- // TODO: generalize
567599 visitor. cx . var_parent = match visitor. cx . extended_parent {
568600 // If the extended parent scope was set, use it.
569- Some ( ExtendedScope ( lifetime ) ) => lifetime ,
601+ Some ( extended_parent ) => extended_parent . to_scope ( & visitor . scope_tree ) . temp_lifetime ,
570602 // Otherwise, like a temporaries, bindings are dropped in the enclosing temporary scope.
571603 None => visitor
572604 . cx
@@ -582,7 +614,7 @@ fn resolve_local<'tcx>(
582614 record_subexpr_extended_temp_scopes (
583615 & mut visitor. scope_tree ,
584616 expr,
585- visitor. cx . var_parent ,
617+ TempLifetime { temp_lifetime : visitor. cx . var_parent , backwards_incompatible : None } ,
586618 ) ;
587619 }
588620
@@ -592,7 +624,8 @@ fn resolve_local<'tcx>(
592624 // When visiting the initializer, extend borrows and `super let`s accessible through
593625 // extending subexpressions to live in the current variable scope (or in the case of
594626 // statics and consts, for the whole program).
595- visitor. cx . extended_parent = Some ( ExtendedScope ( visitor. cx . var_parent ) ) ;
627+ visitor. cx . extended_parent =
628+ Some ( ExtendedScope :: ThroughDeclaration ( visitor. cx . var_parent ) ) ;
596629 }
597630
598631 // Make sure we visit the initializer first.
@@ -694,7 +727,7 @@ fn resolve_local<'tcx>(
694727fn record_subexpr_extended_temp_scopes (
695728 scope_tree : & mut ScopeTree ,
696729 mut expr : & hir:: Expr < ' _ > ,
697- lifetime : Option < Scope > ,
730+ lifetime : TempLifetime ,
698731) {
699732 debug ! ( ?expr, ?lifetime) ;
700733
@@ -741,6 +774,19 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
741774 // account for the destruction scope representing the scope of
742775 // the destructors that run immediately after it completes.
743776 if node_info. drop_temps {
777+ // If this scope corresponds to an extending subexpression, we can extend
778+ // lifetime-extendible temporaries' scopes through it. If we're not already
779+ // lifetime-extending to some larger scope, we set the `extended_parent` to use the
780+ // temporary scope enclosing this node as the scope of lifetime-extended temporaries.
781+ // We only do this for extending subexpressions that also drop their temporaries, rather
782+ // than for all extending subxpressions, so that we only "lifetime-extend" when we're
783+ // actually changing expressions' temporary scopes; this avoids unnecessary insertions
784+ // to the `ScopeTree`'s table of expressions with extended temporary scopes.
785+ if node_info. extending && self . cx . extended_parent . is_none ( ) {
786+ self . cx . extended_parent = Some ( ExtendedScope :: ThroughExpression (
787+ self . cx . parent . expect ( "extending subexpressions should have parent scopes" ) ,
788+ ) ) ;
789+ }
744790 self . enter_scope ( Scope { local_id : id, data : ScopeData :: Destruction } ) ;
745791 }
746792 self . enter_scope ( Scope { local_id : id, data : ScopeData :: Node } ) ;
0 commit comments