Skip to content

Commit

Permalink
Auto merge of #95563 - dingxiangfei2009:dxf-rfc66-refactor, r=nikomat…
Browse files Browse the repository at this point in the history
…sakis

Move the extended lifetime resolution into typeck context

Related to #15023

This PR is based on the [idea](#15023 (comment)) of #15023 by `@nikomatsakis.`

This PR specifically proposes to
- Delay the resolution of scopes of rvalues to a later stage, so that enough type information is available to refine those scopes based on relationships of lifetimes.
- Highlight relevant parts that would help future reviews on the next installments of works to fully implement a solution to RFC 66.
  • Loading branch information
bors committed May 22, 2022
2 parents acfd327 + 6044fbe commit 6534637
Show file tree
Hide file tree
Showing 25 changed files with 363 additions and 192 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1370,7 +1370,7 @@ pub enum UnsafeSource {
UserProvided,
}

#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
pub struct BodyId {
pub hir_id: HirId,
}
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ macro_rules! arena_types {
[] const_allocs: rustc_middle::mir::interpret::Allocation,
// Required for the incremental on-disk cache
[] mir_keys: rustc_hir::def_id::DefIdSet,
[] region_scope_tree: rustc_middle::middle::region::ScopeTree,
[] dropck_outlives:
rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx,
Expand Down
78 changes: 33 additions & 45 deletions compiler/rustc_middle/src/middle/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl Scope {
pub type ScopeDepth = u32;

/// The region scope tree encodes information about region relationships.
#[derive(Default, Debug)]
#[derive(TyEncodable, TyDecodable, Default, Debug)]
pub struct ScopeTree {
/// If not empty, this body is the root of this region hierarchy.
pub root_body: Option<hir::HirId>,
Expand All @@ -223,15 +223,12 @@ pub struct ScopeTree {
/// Maps from a `NodeId` to the associated destruction scope (if any).
destruction_scopes: FxIndexMap<hir::ItemLocalId, Scope>,

/// `rvalue_scopes` includes entries for those expressions whose
/// cleanup scope is larger than the default. The map goes from the
/// expression ID to the cleanup scope id. For rvalues not present in
/// this table, the appropriate cleanup scope is the innermost
/// enclosing statement, conditional expression, or repeating
/// block (see `terminating_scopes`).
/// In constants, None is used to indicate that certain expressions
/// escape into 'static and should have no local cleanup scope.
rvalue_scopes: FxHashMap<hir::ItemLocalId, Option<Scope>>,
/// Identifies expressions which, if captured into a temporary, ought to
/// have a temporary whose lifetime extends to the end of the enclosing *block*,
/// and not the enclosing *statement*. Expressions that are not present in this
/// table are not rvalue candidates. The set of rvalue candidates is computed
/// during type check based on a traversal of the AST.
pub rvalue_candidates: FxHashMap<hir::HirId, RvalueCandidateType>,

/// If there are any `yield` nested within a scope, this map
/// stores the `Span` of the last one and its index in the
Expand Down Expand Up @@ -315,6 +312,17 @@ pub struct ScopeTree {
pub body_expr_count: FxHashMap<hir::BodyId, usize>,
}

/// Identifies the reason that a given expression is an rvalue candidate
/// (see the `rvalue_candidates` field for more information what rvalue
/// candidates in general). In constants, the `lifetime` field is None
/// to indicate that certain expressions escape into 'static and
/// should have no local cleanup scope.
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum RvalueCandidateType {
Borrow { target: hir::ItemLocalId, lifetime: Option<Scope> },
Pattern { target: hir::ItemLocalId, lifetime: Option<Scope> },
}

#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub struct YieldData {
/// The `Span` of the yield.
Expand Down Expand Up @@ -349,12 +357,20 @@ impl ScopeTree {
self.var_map.insert(var, lifetime);
}

pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime);
if let Some(lifetime) = lifetime {
assert!(var != lifetime.item_local_id());
pub fn record_rvalue_candidate(
&mut self,
var: hir::HirId,
candidate_type: RvalueCandidateType,
) {
debug!("record_rvalue_candidate(var={var:?}, type={candidate_type:?})");
match &candidate_type {
RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. }
| RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => {
assert!(var.local_id != lifetime.item_local_id())
}
_ => {}
}
self.rvalue_scopes.insert(var, lifetime);
self.rvalue_candidates.insert(var, candidate_type);
}

/// Returns the narrowest scope that encloses `id`, if any.
Expand All @@ -367,34 +383,6 @@ impl ScopeTree {
self.var_map.get(&var_id).cloned()
}

/// Returns the scope when the temp created by `expr_id` will be cleaned up.
pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> Option<Scope> {
// Check for a designated rvalue scope.
if let Some(&s) = self.rvalue_scopes.get(&expr_id) {
debug!("temporary_scope({:?}) = {:?} [custom]", expr_id, s);
return s;
}

// Otherwise, locate the innermost terminating scope
// if there's one. Static items, for instance, won't
// have an enclosing scope, hence no scope will be
// returned.
let mut id = Scope { id: expr_id, data: ScopeData::Node };

while let Some(&(p, _)) = self.parent_map.get(&id) {
match p.data {
ScopeData::Destruction => {
debug!("temporary_scope({:?}) = {:?} [enclosing]", expr_id, id);
return Some(id);
}
_ => id = p,
}
}

debug!("temporary_scope({:?}) = None", expr_id);
None
}

/// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and
/// `false` otherwise.
///
Expand Down Expand Up @@ -439,7 +427,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
ref parent_map,
ref var_map,
ref destruction_scopes,
ref rvalue_scopes,
ref rvalue_candidates,
ref yield_in_scope,
} = *self;

Expand All @@ -448,7 +436,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
parent_map.hash_stable(hcx, hasher);
var_map.hash_stable(hcx, hasher);
destruction_scopes.hash_stable(hcx, hasher);
rvalue_scopes.hash_stable(hcx, hasher);
rvalue_candidates.hash_stable(hcx, hasher);
yield_in_scope.hash_stable(hcx, hasher);
}
}
6 changes: 0 additions & 6 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,12 +1048,6 @@ rustc_queries! {
desc { "reachability" }
}

/// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body;
/// in the case of closures, this will be redirected to the enclosing function.
query region_scope_tree(def_id: DefId) -> &'tcx region::ScopeTree {
desc { |tcx| "computing drop scopes for `{}`", tcx.def_path_str(def_id) }
}

/// Generates a MIR body for the shim.
query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> {
storage(ArenaCacheSelector<'tcx>)
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::hir::place::Place as HirPlace;
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::region::ScopeTree;
use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath};
use crate::middle::stability;
use crate::mir::interpret::{self, Allocation, ConstAllocation, ConstValue, Scalar};
Expand Down Expand Up @@ -74,6 +75,8 @@ use std::mem;
use std::ops::{Bound, Deref};
use std::sync::Arc;

use super::RvalueScopes;

pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync {
/// Creates a new `OnDiskCache` instance from the serialized data in `data`.
fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self
Expand Down Expand Up @@ -535,6 +538,17 @@ pub struct TypeckResults<'tcx> {
/// issue by fake reading `t`.
pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>>,

/// Tracks critical information about regions in a body.
/// This includes containment relationship between regions,
/// liveness relationship between variables and regions and
/// information about yield points.
pub region_scope_tree: ScopeTree,

/// Tracks the rvalue scoping rules which defines finer scoping for rvalue expressions
/// by applying extended parameter rules.
/// Details may be find in `rustc_typeck::check::rvalue_scopes`.
pub rvalue_scopes: RvalueScopes,

/// Stores the type, expression, span and optional scope span of all types
/// that are live across the yield of this generator (if a generator).
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
Expand Down Expand Up @@ -572,6 +586,8 @@ impl<'tcx> TypeckResults<'tcx> {
concrete_opaque_types: Default::default(),
closure_min_captures: Default::default(),
closure_fake_reads: Default::default(),
region_scope_tree: Default::default(),
rvalue_scopes: Default::default(),
generator_interior_types: ty::Binder::dummy(Default::default()),
treat_byte_string_as_slice: Default::default(),
closure_size_eval: Default::default(),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub use self::context::{
};
pub use self::instance::{Instance, InstanceDef};
pub use self::list::List;
pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::BoundRegionKind::*;
pub use self::sty::RegionKind::*;
pub use self::sty::TyKind::*;
Expand Down Expand Up @@ -118,6 +119,7 @@ mod generics;
mod impls_ty;
mod instance;
mod list;
mod rvalue_scopes;
mod structural_impls;
mod sty;

Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/ty/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use crate::middle::lib_features::LibFeatures;
use crate::middle::privacy::AccessLevels;
use crate::middle::region;
use crate::middle::resolve_lifetime::{
LifetimeScopeForPath, ObjectLifetimeDefault, Region, ResolveLifetimes,
};
Expand Down
57 changes: 57 additions & 0 deletions compiler/rustc_middle/src/ty/rvalue_scopes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::middle::region::{Scope, ScopeData, ScopeTree};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;

/// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by
/// rules laid out in `rustc_typeck::check::rvalue_scopes`.
#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)]
pub struct RvalueScopes {
map: FxHashMap<hir::ItemLocalId, Option<Scope>>,
}

impl RvalueScopes {
pub fn new() -> Self {
Self { map: <_>::default() }
}

/// Returns the scope when the temp created by `expr_id` will be cleaned up.
pub fn temporary_scope(
&self,
region_scope_tree: &ScopeTree,
expr_id: hir::ItemLocalId,
) -> Option<Scope> {
// Check for a designated rvalue scope.
if let Some(&s) = self.map.get(&expr_id) {
debug!("temporary_scope({expr_id:?}) = {s:?} [custom]");
return s;
}

// Otherwise, locate the innermost terminating scope
// if there's one. Static items, for instance, won't
// have an enclosing scope, hence no scope will be
// returned.
let mut id = Scope { id: expr_id, data: ScopeData::Node };

while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
match p.data {
ScopeData::Destruction => {
debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
return Some(id);
}
_ => id = p,
}
}

debug!("temporary_scope({expr_id:?}) = None");
None
}

/// Make an association between a sub-expression and an extended lifetime
pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
if let Some(lifetime) = lifetime {
assert!(var != lifetime.item_local_id());
}
self.map.insert(var, lifetime);
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_mir_build/src/build/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let_scope_stack.push(remainder_scope);

// Declare the bindings, which may create a source scope.
let remainder_span = remainder_scope.span(this.tcx, this.region_scope_tree);
let remainder_span =
remainder_scope.span(this.tcx, &this.typeck_results.region_scope_tree);

let visibility_scope =
Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
// Altough there is almost always scope for given variable in corner cases
// like #92893 we might get variable with no scope.
if let Some(region_scope) = self.region_scope_tree.var_scope(var.local_id) && schedule_drop{
if let Some(region_scope) = self.typeck_results.region_scope_tree.var_scope(var.local_id) && schedule_drop{
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
}
Place::from(local_id)
Expand All @@ -712,7 +712,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
for_guard: ForGuard,
) {
let local_id = self.var_local_id(var, for_guard);
if let Some(region_scope) = self.region_scope_tree.var_scope(var.local_id) {
if let Some(region_scope) = self.typeck_results.region_scope_tree.var_scope(var.local_id) {
self.schedule_drop(span, region_scope, local_id, DropKind::Value);
}
}
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,6 @@ struct Builder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
infcx: &'a InferCtxt<'a, 'tcx>,
typeck_results: &'tcx TypeckResults<'tcx>,
region_scope_tree: &'tcx region::ScopeTree,
param_env: ty::ParamEnv<'tcx>,

thir: &'a Thir<'tcx>,
Expand Down Expand Up @@ -881,7 +880,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
tcx,
infcx,
typeck_results: tcx.typeck_opt_const_arg(def),
region_scope_tree: tcx.region_scope_tree(def.did),
param_env,
def_id: def.did.to_def_id(),
hir_id,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_mir_build/src/build/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}

if scope.region_scope == region_scope {
let region_scope_span = region_scope.span(self.tcx, &self.region_scope_tree);
let region_scope_span =
region_scope.span(self.tcx, &self.typeck_results.region_scope_tree);
// Attribute scope exit drops to scope's closing brace.
let scope_end = self.tcx.sess.source_map().end_point(region_scope_span);

Expand Down
Loading

0 comments on commit 6534637

Please sign in to comment.