Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the extended lifetime resolution into typeck context #95563

Merged
merged 1 commit into from
May 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mark-Simulacrum I think by removing region_scope_tree from the list of queries, I suspect that the caching is lost since this computation could get repeated.

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