Skip to content

Commit 8626bd8

Browse files
committed
only extend temporaries when necessary
1 parent 2c3cafd commit 8626bd8

File tree

3 files changed

+35
-25
lines changed

3 files changed

+35
-25
lines changed

compiler/rustc_hir_analysis/src/check/region.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use rustc_hir::def_id::DefId;
1414
use rustc_hir::intravisit::{self, Visitor};
1515
use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt};
1616
use rustc_index::Idx;
17-
use rustc_middle::bug;
1817
use rustc_middle::middle::region::*;
1918
use rustc_middle::thir::TempLifetime;
2019
use rustc_middle::ty::TyCtxt;
@@ -30,8 +29,9 @@ struct Context {
3029
/// Region parent of expressions, etc.
3130
parent: Option<Scope>,
3231

33-
/// Scope of lifetime-extended temporaries.
34-
extended_parent: ExtendedScope,
32+
/// Scope of lifetime-extended temporaries. If `None`, extendable expressions have their usual
33+
/// temporary scopes. This helps minimize insertions to the [`ScopeTree`]'s `rvalue_scopes` map.
34+
extended_parent: Option<ExtendedScope>,
3535
}
3636

3737
/// Determines the scopes of subexpressions' temporaries.
@@ -56,9 +56,6 @@ enum ExtendedScope {
5656
/// Extendable temporaries will be dropped in the temporary scope enclosing the given scope.
5757
/// This is a separate variant to minimize calls to [`ScopeTree::default_temporary_scope`].
5858
ThroughExpression(Scope),
59-
/// The outermost extended scope is set when visiting a body. To make sure we don't forget to do
60-
/// so, this dummy variant is used when creating the [`ScopeResolutionVisitor`].
61-
Unset,
6259
}
6360

6461
impl ExtendedScope {
@@ -72,7 +69,6 @@ impl ExtendedScope {
7269
scope_tree.default_temporary_scope(non_extending_parent);
7370
TempLifetime { temp_lifetime: Some(temp_scope), backwards_incompatible }
7471
}
75-
ExtendedScope::Unset => bug!("extended parent scope not set"),
7672
}
7773
}
7874
}
@@ -204,6 +200,14 @@ fn resolve_block<'tcx>(
204200
.scope_tree
205201
.backwards_incompatible_scope
206202
.insert(local_id, Scope { local_id, data: ScopeData::Node });
203+
204+
// To avoid false positives in `tail_expr_drop_order`, make sure extendable
205+
// temporaries are extended past the block tail even if that doesn't change their
206+
// scopes in the current edition.
207+
if visitor.cx.extended_parent.is_none() {
208+
visitor.cx.extended_parent =
209+
Some(ExtendedScope::ThroughExpression(visitor.cx.parent.unwrap()));
210+
}
207211
}
208212
resolve_expr(visitor, tail_expr, NodeInfo { drop_temps, extending: true });
209213
}
@@ -320,10 +324,9 @@ fn resolve_expr<'tcx>(
320324
// | E& as ...
321325
match expr.kind {
322326
hir::ExprKind::AddrOf(_, _, subexpr) => {
323-
// Record an extended lifetime if we haven't already done so. This makes sure we don't
324-
// overwrite the scope of e.g. `temp()` in `&*&temp()` when visiting the inner `&`.
325-
if !visitor.scope_tree.has_extended_scope(subexpr.hir_id.local_id) {
326-
let lifetime = visitor.cx.extended_parent.to_scope(&visitor.scope_tree);
327+
// Record an extended lifetime for the operand if needed.
328+
if let Some(extended_scope) = visitor.cx.extended_parent {
329+
let lifetime = extended_scope.to_scope(&visitor.scope_tree);
327330
record_subexpr_rvalue_scopes(&mut visitor.scope_tree, subexpr, lifetime);
328331
}
329332
resolve_expr(visitor, subexpr, NodeInfo { drop_temps: false, extending: true });
@@ -589,8 +592,13 @@ fn resolve_local<'tcx>(
589592
// FIXME(super_let): This ignores backward-incompatible drop hints. Implementing BIDs for
590593
// `super let` bindings could improve `tail_expr_drop_order` with regard to `pin!`, etc.
591594

592-
visitor.cx.var_parent =
593-
visitor.cx.extended_parent.to_scope(&visitor.scope_tree).temp_lifetime;
595+
visitor.cx.var_parent = match visitor.cx.extended_parent {
596+
Some(extended_parent) => extended_parent.to_scope(&visitor.scope_tree).temp_lifetime,
597+
None => visitor
598+
.cx
599+
.var_parent
600+
.map(|block| visitor.scope_tree.default_temporary_scope(block).0),
601+
};
594602
}
595603

596604
if let Some(expr) = init {
@@ -610,7 +618,8 @@ fn resolve_local<'tcx>(
610618
// When visiting the initializer, extend borrows and `super let`s accessible through
611619
// extending subexpressions to live in the current variable scope (or in the case of
612620
// statics and consts, for the whole program).
613-
visitor.cx.extended_parent = ExtendedScope::ThroughDeclaration(visitor.cx.var_parent);
621+
visitor.cx.extended_parent =
622+
Some(ExtendedScope::ThroughDeclaration(visitor.cx.var_parent));
614623
}
615624

616625
// Make sure we visit the initializer first.
@@ -759,14 +768,20 @@ impl<'tcx> ScopeResolutionVisitor<'tcx> {
759768
// account for the destruction scope representing the scope of
760769
// the destructors that run immediately after it completes.
761770
if node_info.drop_temps {
771+
// If this scope corresponds to an extending subexpression, we can extend certain
772+
// temporaries' scopes through it.
773+
if node_info.extending && self.cx.extended_parent.is_none() {
774+
self.cx.extended_parent = Some(ExtendedScope::ThroughExpression(
775+
self.cx.parent.expect("extending subexpressions should have parent scopes"),
776+
));
777+
}
762778
self.enter_scope(Scope { local_id: id, data: ScopeData::Destruction });
763779
}
764780
self.enter_scope(Scope { local_id: id, data: ScopeData::Node });
765781
// If this scope corresponds to a non-extending subexpression, limit the scopes of
766782
// temporaries to the enclosing temporary scope.
767783
if !node_info.extending {
768-
self.cx.extended_parent =
769-
ExtendedScope::ThroughExpression(Scope { local_id: id, data: ScopeData::Node });
784+
self.cx.extended_parent = None;
770785
}
771786
}
772787

@@ -881,7 +896,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
881896
let mut visitor = ScopeResolutionVisitor {
882897
tcx,
883898
scope_tree: ScopeTree::default(),
884-
cx: Context { parent: None, var_parent: None, extended_parent: ExtendedScope::Unset },
899+
cx: Context { parent: None, var_parent: None, extended_parent: None },
885900
};
886901

887902
visitor.scope_tree.root_body = Some(body.value.hir_id);

compiler/rustc_middle/src/middle/region.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,8 @@ impl ScopeTree {
255255
if let Some(lifetime) = lifetime.temp_lifetime {
256256
assert!(var != lifetime.local_id);
257257
}
258-
let result = self.rvalue_scopes.try_insert(var, lifetime);
259-
assert!(result.is_ok());
260-
}
261-
262-
/// Check for an association between a sub-expression and an extended lifetime
263-
pub fn has_extended_scope(&self, var: hir::ItemLocalId) -> bool {
264-
self.rvalue_scopes.contains_key(&var)
258+
let old_lifetime = self.rvalue_scopes.insert(var, lifetime);
259+
assert!(old_lifetime.is_none_or(|old| old == lifetime));
265260
}
266261

267262
/// Returns the narrowest scope that encloses `id`, if any.

compiler/rustc_middle/src/thir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ pub struct Expr<'tcx> {
265265
}
266266

267267
/// Temporary lifetime information for THIR expressions
268-
#[derive(Clone, Copy, Debug, HashStable)]
268+
#[derive(Clone, Copy, Debug, PartialEq, Eq, HashStable)]
269269
pub struct TempLifetime {
270270
/// Lifetime for temporaries as expected.
271271
/// This should be `None` in a constant context.

0 commit comments

Comments
 (0)