Repository navigation
Conversation
Co-authored-by: Travis Cross <tc@traviscross.com>
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
do temporary lifetime extension in a single pass
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (a18535c): comparison URL. Overall result: ❌✅ regressions and improvements - please read:Benchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=iffy rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary 2.3%, secondary 1.7%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary 2.8%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis perf run didn't have relevant results for this metric. Bootstrap: 484.843s -> 488.027s (0.66%) |
|
r? me I will at least do a first pass review on this. |
|
the maybe it'd be worth doing another try build and perf run sometime to see if it stops reporting regressions? I'll leave things as-is for now though |
| hir::ExprKind::Cast(subexpr, _) => { | ||
| resolve_expr(visitor, subexpr, NodeInfo { drop_temps: false, extending: true }); |
There was a problem hiding this comment.
Why extending is set a s true for cast?
There was a problem hiding this comment.
because a cast's operand is extending (or in the Reference's current terminology, if a cast expression is extending, its operand is also extending1)
this can be seen e.g. in
let x = &String::from("string") as &dyn Debug;
println!("{x:?}");since the the String temporary is borrowed by a borrow operator, it can be lifetime-extended past the end of the let statement if it's in an extending position within its parent expression (and that's also in extending position, etc., until reaching the let statement's initializer expression)
note that the terminology I'm using here is a bit of a compromise between the current Reference text and the model #146098 uses2, since this PR sets things up for it
Footnotes
-
https://doc.rust-lang.org/reference/destructors.html#r-destructors.scope.lifetime-extension.exprs ↩
-
under that PR's model, a subexpression being extending means it preserves the scope of lifetime-extendable temporaries from its parent (this is what
extending: truedoes). there's still a difference in how non-extending subexpressions are handled though: this PR keeps the current behavior of not lifetime-extending lifetime-extendable temporaries at all, but in Temporary lifetime extension for blocks #146098, non-extending subexpressions set the scope of lifetime-extendable temporaries to be the enclosing temporary scope at that point (i.e. the scope of a non-extended temporary at that point). this is relevant in cases like{ &temp() }: if that's in an extending position within aletinitializer, aconst, or astatic,temp()outlives the block. otherwise, it's currently dropped at the end of the block. under Temporary lifetime extension for blocks #146098, it always outlives the block (to whatever the scope of lifetime-extendable temporaries is at that point), as if it were scoped as a temporary of whatever super-expression is using it (up to a conservative syntactic approximation). this lets it actually be used, similar to how lifetime extension currently can give temporaries borrowed byletbindings the scopes of those bindings, since that's necessary in order to use the bindings ↩
| /// Region parent of expressions, etc. | ||
| parent: Option<Scope>, |
There was a problem hiding this comment.
What does region parent of expressions mean?
There was a problem hiding this comment.
that's outdated terminology I imagine, probably from before MIR borrowck? in the context of that specific module, it's roughly the parent scope as defined in the Reference1. it differs a little in how it marks temporary scope boundaries. for HIR nodes that drop their temporaries at the end, it adds a ScopeData::Destruction "scope" above the scope for the HIR node to mark it. more nuanced temporary scope boundaries are instead marked with a ScopeData::IfThenRescope or ScopeData::MatchGuard scope. see ScopeTree::default_temporary_scope for how that works
Footnotes
| let prev_extended_parent = visitor.cx.extended_parent; | ||
|
|
||
| if let_kind == LetKind::Regular { | ||
| // When visiting the initializer, extend borrows and `super let`s accessible through | ||
| // extending subexpressions to live in the current variable scope (or in the case of | ||
| // statics and consts, for the whole program). | ||
| visitor.cx.extended_parent = Some(ExtendedScope(visitor.cx.var_parent)); | ||
| } | ||
| } | ||
|
|
||
| // Make sure we visit the initializer first. | ||
| // The correct order, as shared between drop_ranges and intravisitor, | ||
| // is to walk initializer, followed by pattern bindings, finally followed by the `else` block. | ||
| if let Some(expr) = init { | ||
| visitor.visit_expr(expr); | ||
| // Make sure we visit the initializer first. | ||
| // The correct order, as shared between drop_ranges and intravisitor, is | ||
| // to walk initializer, followed by pattern bindings, finally followed by the `else` block. | ||
| resolve_expr(visitor, expr, NodeInfo { drop_temps: false, extending: true }); | ||
|
|
||
| visitor.cx.extended_parent = prev_extended_parent; | ||
| } |
There was a problem hiding this comment.
I find myself being confused about what extended_parent and var_parent are after reading this part 🤔 Do you have an example for this?
There was a problem hiding this comment.
var_parent is the scope used for variables. e.g. variables declared by a normal let statement live to the end of the block
extended_parent is the scope of lifetime-extendable temporaries (used by the operands of borrow expressions and super let bindings). a name more tailored to borrow operators might be better, especially since there's a project goal to move away from super let statements. the idea of tracking a scope for lifetime-extendable temporaries is key to #146098 (which I get into in #165229 (comment))
rather than doing a separate traversal each time we encounter a point we can lifetime-extend through (i.e. a
letstatement initializer or aconst/staticbody), this tracks the scope lifetime-extended temporaries should be extended to in a similar way to how it tracks whether subexpressions introduce temporary scopes.this refactor is the basis for both #146098 and #160297 (the latter of which is part of rust-lang/goals#648), as it makes the things I'm doing there easier to implement. the additional tests are also pulled from #146098, since I expect we want to have them around regardless of that PR being blocked