Skip to content

Commit 9de26a5

Browse files
Auto merge of #148706 - dianne:slightly-lazier-temporary-scoping, r=<try>
compute temporary scopes when building MIR, not THIR
2 parents 87f9dcd + d186978 commit 9de26a5

File tree

28 files changed

+218
-453
lines changed

28 files changed

+218
-453
lines changed

compiler/rustc_hir_analysis/src/check/region.rs

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -516,12 +516,10 @@ fn resolve_local<'tcx>(
516516

517517
if let Some(pat) = pat {
518518
if is_binding_pat(pat) {
519-
visitor.scope_tree.record_rvalue_candidate(
520-
expr.hir_id,
521-
RvalueCandidate {
522-
target: expr.hir_id.local_id,
523-
lifetime: visitor.cx.var_parent,
524-
},
519+
record_subexpr_extended_temp_scopes(
520+
&mut visitor.scope_tree,
521+
expr,
522+
visitor.cx.var_parent,
525523
);
526524
}
527525
}
@@ -604,7 +602,7 @@ fn resolve_local<'tcx>(
604602
}
605603
}
606604

607-
/// If `expr` matches the `E&` grammar, then records an extended rvalue scope as appropriate:
605+
/// If `expr` matches the `E&` grammar, then records an extended temporary scope as appropriate:
608606
///
609607
/// ```text
610608
/// E& = & ET
@@ -627,10 +625,7 @@ fn resolve_local<'tcx>(
627625
match expr.kind {
628626
hir::ExprKind::AddrOf(_, _, subexpr) => {
629627
record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
630-
visitor.scope_tree.record_rvalue_candidate(
631-
subexpr.hir_id,
632-
RvalueCandidate { target: subexpr.hir_id.local_id, lifetime: blk_id },
633-
);
628+
record_subexpr_extended_temp_scopes(&mut visitor.scope_tree, subexpr, blk_id);
634629
}
635630
hir::ExprKind::Struct(_, fields, _) => {
636631
for field in fields {
@@ -687,6 +682,53 @@ fn resolve_local<'tcx>(
687682
}
688683
}
689684

685+
/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by
686+
/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that
687+
/// case, the "temporary lifetime" of `expr` is extended to be the block enclosing the `let`
688+
/// statement.
689+
///
690+
/// More formally, if `expr` matches the grammar `ET`, record the temporary scope of the matching
691+
/// `<rvalue>` as `lifetime`:
692+
///
693+
/// ```text
694+
/// ET = *ET
695+
/// | ET[...]
696+
/// | ET.f
697+
/// | (ET)
698+
/// | <rvalue>
699+
/// ```
700+
///
701+
/// Note: ET is intended to match "rvalues or places based on rvalues".
702+
fn record_subexpr_extended_temp_scopes(
703+
scope_tree: &mut ScopeTree,
704+
mut expr: &hir::Expr<'_>,
705+
lifetime: Option<Scope>,
706+
) {
707+
debug!(?expr, ?lifetime);
708+
709+
loop {
710+
// Note: give all the expressions matching `ET` with the
711+
// extended temporary lifetime, not just the innermost rvalue,
712+
// because in MIR building if we must compile e.g., `*rvalue()`
713+
// into a temporary, we request the temporary scope of the
714+
// outer expression.
715+
716+
scope_tree.record_extended_temp_scope(expr.hir_id.local_id, lifetime);
717+
718+
match expr.kind {
719+
hir::ExprKind::AddrOf(_, _, subexpr)
720+
| hir::ExprKind::Unary(hir::UnOp::Deref, subexpr)
721+
| hir::ExprKind::Field(subexpr, _)
722+
| hir::ExprKind::Index(subexpr, _, _) => {
723+
expr = subexpr;
724+
}
725+
_ => {
726+
return;
727+
}
728+
}
729+
}
730+
}
731+
690732
impl<'tcx> ScopeResolutionVisitor<'tcx> {
691733
/// Records the current parent (if any) as the parent of `child_scope`.
692734
fn record_child_scope(&mut self, child_scope: Scope) {

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use tracing::{debug, instrument};
4040
use crate::callee::{self, DeferredCallResolution};
4141
use crate::errors::{self, CtorIsPrivate};
4242
use crate::method::{self, MethodCallee};
43-
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, rvalue_scopes};
43+
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};
4444

4545
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4646
/// Produces warning on the given node, if the current point in the
@@ -604,13 +604,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
604604
self.normalize(span, field.ty(self.tcx, args))
605605
}
606606

607-
pub(crate) fn resolve_rvalue_scopes(&self, def_id: DefId) {
608-
let scope_tree = self.tcx.region_scope_tree(def_id);
609-
let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, scope_tree, def_id) };
610-
let mut typeck_results = self.typeck_results.borrow_mut();
611-
typeck_results.rvalue_scopes = rvalue_scopes;
612-
}
613-
614607
/// Drain all obligations that are stalled on coroutines defined in this body.
615608
#[instrument(level = "debug", skip(self))]
616609
pub(crate) fn drain_stalled_coroutine_obligations(&self) {

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ mod op;
3737
mod opaque_types;
3838
mod pat;
3939
mod place_op;
40-
mod rvalue_scopes;
4140
mod typeck_root_ctxt;
4241
mod upvar;
4342
mod writeback;
@@ -237,9 +236,6 @@ fn typeck_with_inspect<'tcx>(
237236
// because they don't constrain other type variables.
238237
fcx.closure_analyze(body);
239238
assert!(fcx.deferred_call_resolutions.borrow().is_empty());
240-
// Before the coroutine analysis, temporary scopes shall be marked to provide more
241-
// precise information on types to be captured.
242-
fcx.resolve_rvalue_scopes(def_id.to_def_id());
243239

244240
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
245241
let ty = fcx.normalize(span, ty);

compiler/rustc_hir_typeck/src/rvalue_scopes.rs

Lines changed: 0 additions & 79 deletions
This file was deleted.

compiler/rustc_hir_typeck/src/writeback.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7979
wbcx.visit_offset_of_container_types();
8080
wbcx.visit_potentially_region_dependent_goals();
8181

82-
wbcx.typeck_results.rvalue_scopes =
83-
mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes);
84-
8582
let used_trait_imports =
8683
mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports);
8784
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);

compiler/rustc_middle/src/middle/region.rs

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::fmt;
1111
use rustc_data_structures::fx::FxIndexMap;
1212
use rustc_data_structures::unord::UnordMap;
1313
use rustc_hir as hir;
14-
use rustc_hir::{HirId, HirIdMap, Node};
14+
use rustc_hir::{HirId, ItemLocalMap, Node};
1515
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
1616
use rustc_span::{DUMMY_SP, Span};
1717
use tracing::debug;
@@ -221,27 +221,28 @@ pub struct ScopeTree {
221221
/// variable is declared.
222222
var_map: FxIndexMap<hir::ItemLocalId, Scope>,
223223

224-
/// Identifies expressions which, if captured into a temporary, ought to
225-
/// have a temporary whose lifetime extends to the end of the enclosing *block*,
226-
/// and not the enclosing *statement*. Expressions that are not present in this
227-
/// table are not rvalue candidates. The set of rvalue candidates is computed
228-
/// during type check based on a traversal of the AST.
229-
pub rvalue_candidates: HirIdMap<RvalueCandidate>,
224+
/// Tracks expressions with extended temporary scopes, based on the syntactic rules for
225+
/// temporary lifetime extension. Further details may be found in
226+
/// `rustc_hir_analysis::check::region` and in the [Reference].
227+
///
228+
/// [Reference]: https://doc.rust-lang.org/nightly/reference/destructors.html#temporary-lifetime-extension
229+
extended_temp_scopes: ItemLocalMap<Option<Scope>>,
230230

231231
/// Backwards incompatible scoping that will be introduced in future editions.
232232
/// This information is used later for linting to identify locals and
233233
/// temporary values that will receive backwards-incompatible drop orders.
234234
pub backwards_incompatible_scope: UnordMap<hir::ItemLocalId, Scope>,
235235
}
236236

237-
/// See the `rvalue_candidates` field for more information on rvalue
238-
/// candidates in general.
239-
/// The `lifetime` field is None to indicate that certain expressions escape
240-
/// into 'static and should have no local cleanup scope.
241-
#[derive(Debug, Copy, Clone, HashStable)]
242-
pub struct RvalueCandidate {
243-
pub target: hir::ItemLocalId,
244-
pub lifetime: Option<Scope>,
237+
/// Temporary lifetime information for expressions, used when lowering to MIR.
238+
#[derive(Clone, Copy, Debug, HashStable)]
239+
pub struct TempLifetime {
240+
/// The scope in which a temporary should be dropped. If `None`, no drop is scheduled; this is
241+
/// the case for lifetime-extended temporaries extended by a const/static item or const block.
242+
pub temp_lifetime: Option<Scope>,
243+
/// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition.
244+
/// If `None`, then no changes are expected, or lints are disabled.
245+
pub backwards_incompatible: Option<Scope>,
245246
}
246247

247248
impl ScopeTree {
@@ -260,12 +261,13 @@ impl ScopeTree {
260261
self.var_map.insert(var, lifetime);
261262
}
262263

263-
pub fn record_rvalue_candidate(&mut self, var: HirId, candidate: RvalueCandidate) {
264-
debug!("record_rvalue_candidate(var={var:?}, candidate={candidate:?})");
265-
if let Some(lifetime) = &candidate.lifetime {
266-
assert!(var.local_id != lifetime.local_id)
264+
/// Make an association between a sub-expression and an extended lifetime
265+
pub fn record_extended_temp_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
266+
debug!(?var, ?lifetime);
267+
if let Some(lifetime) = lifetime {
268+
assert!(var != lifetime.local_id);
267269
}
268-
self.rvalue_candidates.insert(var, candidate);
270+
self.extended_temp_scopes.insert(var, lifetime);
269271
}
270272

271273
/// Returns the narrowest scope that encloses `id`, if any.
@@ -337,4 +339,20 @@ impl ScopeTree {
337339

338340
span_bug!(ty::tls::with(|tcx| inner.span(tcx, self)), "no enclosing temporary scope")
339341
}
342+
343+
/// Returns the scope when the temp created by `expr_id` will be cleaned up.
344+
/// It also emits a lint on potential backwards incompatible change to the temporary scope
345+
/// which is *for now* always shortening.
346+
pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> TempLifetime {
347+
// Check for a designated extended temporary scope.
348+
if let Some(&s) = self.extended_temp_scopes.get(&expr_id) {
349+
debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
350+
return TempLifetime { temp_lifetime: s, backwards_incompatible: None };
351+
}
352+
353+
// Otherwise, locate the innermost terminating scope.
354+
let (scope, backwards_incompatible) =
355+
self.default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node });
356+
TempLifetime { temp_lifetime: Some(scope), backwards_incompatible }
357+
}
340358
}

compiler/rustc_middle/src/thir.rs

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -256,25 +256,15 @@ pub struct Expr<'tcx> {
256256
/// The type of this expression
257257
pub ty: Ty<'tcx>,
258258

259-
/// The lifetime of this expression if it should be spilled into a
260-
/// temporary
261-
pub temp_lifetime: TempLifetime,
259+
/// The id of the HIR expression whose [temporary scope] should be used for this expression.
260+
///
261+
/// [temporary scope]: https://doc.rust-lang.org/reference/destructors.html#temporary-scopes
262+
pub temp_scope_id: hir::ItemLocalId,
262263

263264
/// span of the expression in the source
264265
pub span: Span,
265266
}
266267

267-
/// Temporary lifetime information for THIR expressions
268-
#[derive(Clone, Copy, Debug, HashStable)]
269-
pub struct TempLifetime {
270-
/// Lifetime for temporaries as expected.
271-
/// This should be `None` in a constant context.
272-
pub temp_lifetime: Option<region::Scope>,
273-
/// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition.
274-
/// If `None`, then no changes are expected, or lints are disabled.
275-
pub backwards_incompatible: Option<region::Scope>,
276-
}
277-
278268
#[derive(Clone, Debug, HashStable)]
279269
pub enum ExprKind<'tcx> {
280270
/// `Scope`s are used to explicitly mark destruction scopes,
@@ -1127,7 +1117,7 @@ mod size_asserts {
11271117
use super::*;
11281118
// tidy-alphabetical-start
11291119
static_assert_size!(Block, 48);
1130-
static_assert_size!(Expr<'_>, 72);
1120+
static_assert_size!(Expr<'_>, 64);
11311121
static_assert_size!(ExprKind<'_>, 40);
11321122
static_assert_size!(Pat<'_>, 64);
11331123
static_assert_size!(PatKind<'_>, 48);

compiler/rustc_middle/src/thir/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
4545
expr: &'thir Expr<'tcx>,
4646
) {
4747
use ExprKind::*;
48-
let Expr { kind, ty: _, temp_lifetime: _, span: _ } = expr;
48+
let Expr { kind, ty: _, temp_scope_id: _, span: _ } = expr;
4949
match *kind {
5050
Scope { value, region_scope: _, lint_level: _ } => {
5151
visitor.visit_expr(&visitor.thir()[value])

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ pub use self::region::{
9797
BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region,
9898
RegionKind, RegionVid,
9999
};
100-
pub use self::rvalue_scopes::RvalueScopes;
101100
pub use self::sty::{
102101
AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
103102
CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
@@ -156,7 +155,6 @@ mod list;
156155
mod opaque_types;
157156
mod predicate;
158157
mod region;
159-
mod rvalue_scopes;
160158
mod structural_impls;
161159
#[allow(hidden_glob_reexports)]
162160
mod sty;

0 commit comments

Comments
 (0)