Make body-visiting logic reusable

This commit is contained in:
Oli Scherer 2024-05-24 09:32:20 +00:00
parent be94ca0bcd
commit 53e3c3271f
1 changed files with 59 additions and 53 deletions

View File

@ -782,6 +782,32 @@ impl<'tcx> RegionResolutionVisitor<'tcx> {
} }
self.enter_scope(Scope { id, data: ScopeData::Node }); self.enter_scope(Scope { id, data: ScopeData::Node });
} }
fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) {
// Save all state that is specific to the outer function
// body. These will be restored once down below, once we've
// visited the body.
let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0);
let outer_cx = self.cx;
let outer_ts = mem::take(&mut self.terminating_scopes);
// The 'pessimistic yield' flag is set to true when we are
// processing a `+=` statement and have to make pessimistic
// control flow assumptions. This doesn't apply to nested
// bodies within the `+=` statements. See #69307.
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
self.terminating_scopes.insert(hir_id.local_id);
self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::CallSite });
self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::Arguments });
f(self);
// Restore context we had at the start.
self.expr_and_pat_count = outer_ec;
self.cx = outer_cx;
self.terminating_scopes = outer_ts;
self.pessimistic_yield = outer_pessimistic_yield;
}
} }
impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
@ -801,60 +827,40 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
self.cx.parent self.cx.parent
); );
// Save all state that is specific to the outer function self.enter_body(body.value.hir_id, |this| {
// body. These will be restored once down below, once we've if this.tcx.hir().body_owner_kind(owner_id).is_fn_or_closure() {
// visited the body. // The arguments and `self` are parented to the fn.
let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0); this.cx.var_parent = this.cx.parent.take();
let outer_cx = self.cx; for param in body.params {
let outer_ts = mem::take(&mut self.terminating_scopes); this.visit_pat(param.pat);
// The 'pessimistic yield' flag is set to true when we are }
// processing a `+=` statement and have to make pessimistic
// control flow assumptions. This doesn't apply to nested
// bodies within the `+=` statements. See #69307.
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
self.terminating_scopes.insert(body.value.hir_id.local_id);
self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite }); // The body of the every fn is a root scope.
self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments }); this.cx.parent = this.cx.var_parent;
this.visit_expr(body.value)
// The arguments and `self` are parented to the fn. } else {
self.cx.var_parent = self.cx.parent.take(); // Only functions have an outer terminating (drop) scope, while
for param in body.params { // temporaries in constant initializers may be 'static, but only
self.visit_pat(param.pat); // according to rvalue lifetime semantics, using the same
} // syntactical rules used for let initializers.
//
// The body of the every fn is a root scope. // e.g., in `let x = &f();`, the temporary holding the result from
self.cx.parent = self.cx.var_parent; // the `f()` call lives for the entirety of the surrounding block.
if self.tcx.hir().body_owner_kind(owner_id).is_fn_or_closure() { //
self.visit_expr(body.value) // Similarly, `const X: ... = &f();` would have the result of `f()`
} else { // live for `'static`, implying (if Drop restrictions on constants
// Only functions have an outer terminating (drop) scope, while // ever get lifted) that the value *could* have a destructor, but
// temporaries in constant initializers may be 'static, but only // it'd get leaked instead of the destructor running during the
// according to rvalue lifetime semantics, using the same // evaluation of `X` (if at all allowed by CTFE).
// syntactical rules used for let initializers. //
// // However, `const Y: ... = g(&f());`, like `let y = g(&f());`,
// e.g., in `let x = &f();`, the temporary holding the result from // would *not* let the `f()` temporary escape into an outer scope
// the `f()` call lives for the entirety of the surrounding block. // (i.e., `'static`), which means that after `g` returns, it drops,
// // and all the associated destruction scope rules apply.
// Similarly, `const X: ... = &f();` would have the result of `f()` this.cx.var_parent = None;
// live for `'static`, implying (if Drop restrictions on constants resolve_local(this, None, Some(body.value));
// ever get lifted) that the value *could* have a destructor, but }
// it'd get leaked instead of the destructor running during the })
// evaluation of `X` (if at all allowed by CTFE).
//
// However, `const Y: ... = g(&f());`, like `let y = g(&f());`,
// would *not* let the `f()` temporary escape into an outer scope
// (i.e., `'static`), which means that after `g` returns, it drops,
// and all the associated destruction scope rules apply.
self.cx.var_parent = None;
resolve_local(self, None, Some(body.value));
}
// Restore context we had at the start.
self.expr_and_pat_count = outer_ec;
self.cx = outer_cx;
self.terminating_scopes = outer_ts;
self.pessimistic_yield = outer_pessimistic_yield;
} }
fn visit_arm(&mut self, a: &'tcx Arm<'tcx>) { fn visit_arm(&mut self, a: &'tcx Arm<'tcx>) {