factor out the rvalue lifetime rule
remove region_scope_tree from RegionCtxt Apply suggestions from code review Co-authored-by: Niko Matsakis <niko@alum.mit.edu>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
///
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>)
|
||||
|
||||
@@ -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};
|
||||
@@ -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
|
||||
@@ -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>>>,
|
||||
@@ -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(),
|
||||
|
||||
@@ -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::*;
|
||||
@@ -118,6 +119,7 @@ mod generics;
|
||||
mod impls_ty;
|
||||
mod instance;
|
||||
mod list;
|
||||
mod rvalue_scopes;
|
||||
mod structural_impls;
|
||||
mod sty;
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
57
compiler/rustc_middle/src/ty/rvalue_scopes.rs
Normal file
57
compiler/rustc_middle/src/ty/rvalue_scopes.rs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user