Auto merge of #95563 - dingxiangfei2009:dxf-rfc66-refactor, r=nikomatsakis

Move the extended lifetime resolution into typeck context

Related to #15023

This PR is based on the [idea](https://github.com/rust-lang/rust/issues/15023#issuecomment-1070931433) 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.
This commit is contained in:
bors 2022-05-22 09:00:30 +00:00
commit 653463731a
25 changed files with 363 additions and 192 deletions

View File

@ -1370,7 +1370,7 @@ pub enum UnsafeSource {
UserProvided, UserProvided,
} }
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
pub struct BodyId { pub struct BodyId {
pub hir_id: HirId, pub hir_id: HirId,
} }

View File

@ -33,7 +33,6 @@ macro_rules! arena_types {
[] const_allocs: rustc_middle::mir::interpret::Allocation, [] const_allocs: rustc_middle::mir::interpret::Allocation,
// Required for the incremental on-disk cache // Required for the incremental on-disk cache
[] mir_keys: rustc_hir::def_id::DefIdSet, [] mir_keys: rustc_hir::def_id::DefIdSet,
[] region_scope_tree: rustc_middle::middle::region::ScopeTree,
[] dropck_outlives: [] dropck_outlives:
rustc_middle::infer::canonical::Canonical<'tcx, rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::infer::canonical::QueryResponse<'tcx,

View File

@ -203,7 +203,7 @@ impl Scope {
pub type ScopeDepth = u32; pub type ScopeDepth = u32;
/// The region scope tree encodes information about region relationships. /// The region scope tree encodes information about region relationships.
#[derive(Default, Debug)] #[derive(TyEncodable, TyDecodable, Default, Debug)]
pub struct ScopeTree { pub struct ScopeTree {
/// If not empty, this body is the root of this region hierarchy. /// If not empty, this body is the root of this region hierarchy.
pub root_body: Option<hir::HirId>, pub root_body: Option<hir::HirId>,
@ -223,15 +223,12 @@ pub struct ScopeTree {
/// Maps from a `NodeId` to the associated destruction scope (if any). /// Maps from a `NodeId` to the associated destruction scope (if any).
destruction_scopes: FxIndexMap<hir::ItemLocalId, Scope>, destruction_scopes: FxIndexMap<hir::ItemLocalId, Scope>,
/// `rvalue_scopes` includes entries for those expressions whose /// Identifies expressions which, if captured into a temporary, ought to
/// cleanup scope is larger than the default. The map goes from the /// have a temporary whose lifetime extends to the end of the enclosing *block*,
/// expression ID to the cleanup scope id. For rvalues not present in /// and not the enclosing *statement*. Expressions that are not present in this
/// this table, the appropriate cleanup scope is the innermost /// table are not rvalue candidates. The set of rvalue candidates is computed
/// enclosing statement, conditional expression, or repeating /// during type check based on a traversal of the AST.
/// block (see `terminating_scopes`). pub rvalue_candidates: FxHashMap<hir::HirId, RvalueCandidateType>,
/// 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>>,
/// If there are any `yield` nested within a scope, this map /// If there are any `yield` nested within a scope, this map
/// stores the `Span` of the last one and its index in the /// 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>, 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)] #[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub struct YieldData { pub struct YieldData {
/// The `Span` of the yield. /// The `Span` of the yield.
@ -349,12 +357,20 @@ impl ScopeTree {
self.var_map.insert(var, lifetime); self.var_map.insert(var, lifetime);
} }
pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) { pub fn record_rvalue_candidate(
debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime); &mut self,
if let Some(lifetime) = lifetime { var: hir::HirId,
assert!(var != lifetime.item_local_id()); 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. /// Returns the narrowest scope that encloses `id`, if any.
@ -367,34 +383,6 @@ impl ScopeTree {
self.var_map.get(&var_id).cloned() 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 /// Returns `true` if `subscope` is equal to or is lexically nested inside `superscope`, and
/// `false` otherwise. /// `false` otherwise.
/// ///
@ -439,7 +427,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
ref parent_map, ref parent_map,
ref var_map, ref var_map,
ref destruction_scopes, ref destruction_scopes,
ref rvalue_scopes, ref rvalue_candidates,
ref yield_in_scope, ref yield_in_scope,
} = *self; } = *self;
@ -448,7 +436,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
parent_map.hash_stable(hcx, hasher); parent_map.hash_stable(hcx, hasher);
var_map.hash_stable(hcx, hasher); var_map.hash_stable(hcx, hasher);
destruction_scopes.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); yield_in_scope.hash_stable(hcx, hasher);
} }
} }

View File

@ -1048,12 +1048,6 @@ rustc_queries! {
desc { "reachability" } 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. /// Generates a MIR body for the shim.
query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> { query mir_shims(key: ty::InstanceDef<'tcx>) -> mir::Body<'tcx> {
storage(ArenaCacheSelector<'tcx>) storage(ArenaCacheSelector<'tcx>)

View File

@ -6,6 +6,7 @@ use crate::hir::place::Place as HirPlace;
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::region::ScopeTree;
use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath}; use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath};
use crate::middle::stability; use crate::middle::stability;
use crate::mir::interpret::{self, Allocation, ConstAllocation, ConstValue, Scalar}; use crate::mir::interpret::{self, Allocation, ConstAllocation, ConstValue, Scalar};
@ -74,6 +75,8 @@ use std::mem;
use std::ops::{Bound, Deref}; use std::ops::{Bound, Deref};
use std::sync::Arc; use std::sync::Arc;
use super::RvalueScopes;
pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync {
/// Creates a new `OnDiskCache` instance from the serialized data in `data`. /// Creates a new `OnDiskCache` instance from the serialized data in `data`.
fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self
@ -535,6 +538,17 @@ pub struct TypeckResults<'tcx> {
/// issue by fake reading `t`. /// issue by fake reading `t`.
pub closure_fake_reads: FxHashMap<DefId, Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>>, 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 /// Stores the type, expression, span and optional scope span of all types
/// that are live across the yield of this generator (if a generator). /// that are live across the yield of this generator (if a generator).
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>, pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
@ -572,6 +586,8 @@ impl<'tcx> TypeckResults<'tcx> {
concrete_opaque_types: Default::default(), concrete_opaque_types: Default::default(),
closure_min_captures: Default::default(), closure_min_captures: Default::default(),
closure_fake_reads: Default::default(), closure_fake_reads: Default::default(),
region_scope_tree: Default::default(),
rvalue_scopes: Default::default(),
generator_interior_types: ty::Binder::dummy(Default::default()), generator_interior_types: ty::Binder::dummy(Default::default()),
treat_byte_string_as_slice: Default::default(), treat_byte_string_as_slice: Default::default(),
closure_size_eval: Default::default(), closure_size_eval: Default::default(),

View File

@ -72,6 +72,7 @@ pub use self::context::{
}; };
pub use self::instance::{Instance, InstanceDef}; pub use self::instance::{Instance, InstanceDef};
pub use self::list::List; pub use self::list::List;
pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::BoundRegionKind::*; pub use self::sty::BoundRegionKind::*;
pub use self::sty::RegionKind::*; pub use self::sty::RegionKind::*;
pub use self::sty::TyKind::*; pub use self::sty::TyKind::*;
@ -118,6 +119,7 @@ mod generics;
mod impls_ty; mod impls_ty;
mod instance; mod instance;
mod list; mod list;
mod rvalue_scopes;
mod structural_impls; mod structural_impls;
mod sty; mod sty;

View File

@ -6,7 +6,6 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use crate::middle::lib_features::LibFeatures; use crate::middle::lib_features::LibFeatures;
use crate::middle::privacy::AccessLevels; use crate::middle::privacy::AccessLevels;
use crate::middle::region;
use crate::middle::resolve_lifetime::{ use crate::middle::resolve_lifetime::{
LifetimeScopeForPath, ObjectLifetimeDefault, Region, ResolveLifetimes, LifetimeScopeForPath, ObjectLifetimeDefault, Region, ResolveLifetimes,
}; };

View 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);
}
}

View File

@ -108,7 +108,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let_scope_stack.push(remainder_scope); let_scope_stack.push(remainder_scope);
// Declare the bindings, which may create a source 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 = let visibility_scope =
Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None)); Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));

View File

@ -699,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
// Altough there is almost always scope for given variable in corner cases // Altough there is almost always scope for given variable in corner cases
// like #92893 we might get variable with no scope. // 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); self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
} }
Place::from(local_id) Place::from(local_id)
@ -712,7 +712,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
for_guard: ForGuard, for_guard: ForGuard,
) { ) {
let local_id = self.var_local_id(var, for_guard); 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); self.schedule_drop(span, region_scope, local_id, DropKind::Value);
} }
} }

View File

@ -398,7 +398,6 @@ struct Builder<'a, 'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
infcx: &'a InferCtxt<'a, 'tcx>, infcx: &'a InferCtxt<'a, 'tcx>,
typeck_results: &'tcx TypeckResults<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>,
region_scope_tree: &'tcx region::ScopeTree,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
thir: &'a Thir<'tcx>, thir: &'a Thir<'tcx>,
@ -881,7 +880,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
tcx, tcx,
infcx, infcx,
typeck_results: tcx.typeck_opt_const_arg(def), typeck_results: tcx.typeck_opt_const_arg(def),
region_scope_tree: tcx.region_scope_tree(def.did),
param_env, param_env,
def_id: def.did.to_def_id(), def_id: def.did.to_def_id(),
hir_id, hir_id,

View File

@ -916,7 +916,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
if scope.region_scope == region_scope { 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. // Attribute scope exit drops to scope's closing brace.
let scope_end = self.tcx.sess.source_map().end_point(region_scope_span); let scope_end = self.tcx.sess.source_map().end_point(region_scope_span);

View File

@ -32,7 +32,8 @@ impl<'tcx> Cx<'tcx> {
} }
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId { pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
let temp_lifetime = self.region_scope_tree.temporary_scope(hir_expr.hir_id.local_id); let temp_lifetime =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id);
let expr_scope = let expr_scope =
region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
@ -161,7 +162,8 @@ impl<'tcx> Cx<'tcx> {
let tcx = self.tcx; let tcx = self.tcx;
let expr_ty = self.typeck_results().expr_ty(expr); let expr_ty = self.typeck_results().expr_ty(expr);
let expr_span = expr.span; let expr_span = expr.span;
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); let temp_lifetime =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
let kind = match expr.kind { let kind = match expr.kind {
// Here comes the interesting stuff: // Here comes the interesting stuff:
@ -575,7 +577,9 @@ impl<'tcx> Cx<'tcx> {
}, },
hir::ExprKind::Loop(ref body, ..) => { hir::ExprKind::Loop(ref body, ..) => {
let block_ty = self.typeck_results().node_type(body.hir_id); let block_ty = self.typeck_results().node_type(body.hir_id);
let temp_lifetime = self.region_scope_tree.temporary_scope(body.hir_id.local_id); let temp_lifetime = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, body.hir_id.local_id);
let block = self.mirror_block(body); let block = self.mirror_block(body);
let body = self.thir.exprs.push(Expr { let body = self.thir.exprs.push(Expr {
ty: block_ty, ty: block_ty,
@ -776,7 +780,8 @@ impl<'tcx> Cx<'tcx> {
span: Span, span: Span,
overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>,
) -> Expr<'tcx> { ) -> Expr<'tcx> {
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); let temp_lifetime =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
let (def_id, substs, user_ty) = match overloaded_callee { let (def_id, substs, user_ty) = match overloaded_callee {
Some((def_id, substs)) => (def_id, substs, None), Some((def_id, substs)) => (def_id, substs, None),
None => { None => {
@ -863,7 +868,9 @@ impl<'tcx> Cx<'tcx> {
// a constant reference (or constant raw pointer for `static mut`) in MIR // a constant reference (or constant raw pointer for `static mut`) in MIR
Res::Def(DefKind::Static(_), id) => { Res::Def(DefKind::Static(_), id) => {
let ty = self.tcx.static_ptr_ty(id); let ty = self.tcx.static_ptr_ty(id);
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); let temp_lifetime = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
let kind = if self.tcx.is_thread_local_static(id) { let kind = if self.tcx.is_thread_local_static(id) {
ExprKind::ThreadLocalRef(id) ExprKind::ThreadLocalRef(id)
} else { } else {
@ -939,7 +946,8 @@ impl<'tcx> Cx<'tcx> {
// construct the complete expression `foo()` for the overloaded call, // construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type // which will yield the &T type
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); let temp_lifetime =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
let fun = self.method_callee(expr, span, overloaded_callee); let fun = self.method_callee(expr, span, overloaded_callee);
let fun = self.thir.exprs.push(fun); let fun = self.thir.exprs.push(fun);
let fun_ty = self.thir[fun].ty; let fun_ty = self.thir[fun].ty;
@ -959,7 +967,9 @@ impl<'tcx> Cx<'tcx> {
closure_expr: &'tcx hir::Expr<'tcx>, closure_expr: &'tcx hir::Expr<'tcx>,
place: HirPlace<'tcx>, place: HirPlace<'tcx>,
) -> Expr<'tcx> { ) -> Expr<'tcx> {
let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); let temp_lifetime = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
let var_ty = place.base_ty; let var_ty = place.base_ty;
// The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path // The result of capture analysis in `rustc_typeck/check/upvar.rs`represents a captured path
@ -1014,7 +1024,9 @@ impl<'tcx> Cx<'tcx> {
let upvar_capture = captured_place.info.capture_kind; let upvar_capture = captured_place.info.capture_kind;
let captured_place_expr = let captured_place_expr =
self.convert_captured_hir_place(closure_expr, captured_place.place.clone()); self.convert_captured_hir_place(closure_expr, captured_place.place.clone());
let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id); let temp_lifetime = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id);
match upvar_capture { match upvar_capture {
ty::UpvarCapture::ByValue => captured_place_expr, ty::UpvarCapture::ByValue => captured_place_expr,

View File

@ -16,7 +16,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
use rustc_middle::mir::ConstantKind; use rustc_middle::mir::ConstantKind;
use rustc_middle::thir::*; use rustc_middle::thir::*;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
use rustc_span::Span; use rustc_span::Span;
pub(crate) fn thir_body<'tcx>( pub(crate) fn thir_body<'tcx>(
@ -51,6 +51,7 @@ struct Cx<'tcx> {
pub(crate) region_scope_tree: &'tcx region::ScopeTree, pub(crate) region_scope_tree: &'tcx region::ScopeTree,
pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>, pub(crate) typeck_results: &'tcx ty::TypeckResults<'tcx>,
pub(crate) rvalue_scopes: &'tcx RvalueScopes,
/// When applying adjustments to the expression /// When applying adjustments to the expression
/// with the given `HirId`, use the given `Span`, /// with the given `HirId`, use the given `Span`,
@ -71,8 +72,9 @@ impl<'tcx> Cx<'tcx> {
tcx, tcx,
thir: Thir::new(), thir: Thir::new(),
param_env: tcx.param_env(def.did), param_env: tcx.param_env(def.did),
region_scope_tree: tcx.region_scope_tree(def.did), region_scope_tree: &typeck_results.region_scope_tree,
typeck_results, typeck_results,
rvalue_scopes: &typeck_results.rvalue_scopes,
body_owner: def.did.to_def_id(), body_owner: def.did.to_def_id(),
adjustment_span: None, adjustment_span: None,
} }

View File

@ -38,7 +38,6 @@ mod liveness;
pub mod loops; pub mod loops;
mod naked_functions; mod naked_functions;
mod reachable; mod reachable;
mod region;
pub mod stability; pub mod stability;
mod upvars; mod upvars;
mod weak_lang_items; mod weak_lang_items;
@ -57,7 +56,6 @@ pub fn provide(providers: &mut Providers) {
liveness::provide(providers); liveness::provide(providers);
intrinsicck::provide(providers); intrinsicck::provide(providers);
reachable::provide(providers); reachable::provide(providers);
region::provide(providers);
stability::provide(providers); stability::provide(providers);
upvars::provide(providers); upvars::provide(providers);
} }

View File

@ -4,6 +4,7 @@ use crate::astconv::{
}; };
use crate::check::callee::{self, DeferredCallResolution}; use crate::check::callee::{self, DeferredCallResolution};
use crate::check::method::{self, MethodCallee, SelfSource}; use crate::check::method::{self, MethodCallee, SelfSource};
use crate::check::{region, rvalue_scopes};
use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
@ -620,6 +621,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) self.normalize_associated_types_in(span, field.ty(self.tcx, substs))
} }
pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) {
let scope_tree = region::region_scope_tree(self.tcx, def_id);
let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, &scope_tree, def_id) };
let mut typeck_results = self.inh.typeck_results.borrow_mut();
typeck_results.region_scope_tree = scope_tree;
typeck_results.rvalue_scopes = rvalue_scopes;
}
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
let mut generators = self.deferred_generator_interiors.borrow_mut(); let mut generators = self.deferred_generator_interiors.borrow_mut();
for (body_id, interior, kind) in generators.drain(..) { for (body_id, interior, kind) in generators.drain(..) {

View File

@ -14,7 +14,7 @@ use rustc_hir::hir_id::HirIdSet;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span; use rustc_span::Span;
use tracing::debug; use tracing::debug;
@ -23,8 +23,9 @@ mod drop_ranges;
struct InteriorVisitor<'a, 'tcx> { struct InteriorVisitor<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>, fcx: &'a FnCtxt<'a, 'tcx>,
region_scope_tree: &'a region::ScopeTree,
types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>, types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
region_scope_tree: &'tcx region::ScopeTree, rvalue_scopes: &'a RvalueScopes,
expr_count: usize, expr_count: usize,
kind: hir::GeneratorKind, kind: hir::GeneratorKind,
prev_unresolved_span: Option<Span>, prev_unresolved_span: Option<Span>,
@ -179,10 +180,12 @@ pub fn resolve_interior<'a, 'tcx>(
kind: hir::GeneratorKind, kind: hir::GeneratorKind,
) { ) {
let body = fcx.tcx.hir().body(body_id); let body = fcx.tcx.hir().body(body_id);
let typeck_results = fcx.inh.typeck_results.borrow();
let mut visitor = InteriorVisitor { let mut visitor = InteriorVisitor {
fcx, fcx,
types: FxIndexSet::default(), types: FxIndexSet::default(),
region_scope_tree: fcx.tcx.region_scope_tree(def_id), region_scope_tree: &typeck_results.region_scope_tree,
rvalue_scopes: &typeck_results.rvalue_scopes,
expr_count: 0, expr_count: 0,
kind, kind,
prev_unresolved_span: None, prev_unresolved_span: None,
@ -192,7 +195,7 @@ pub fn resolve_interior<'a, 'tcx>(
intravisit::walk_body(&mut visitor, body); intravisit::walk_body(&mut visitor, body);
// Check that we visited the same amount of expressions as the RegionResolutionVisitor // Check that we visited the same amount of expressions as the RegionResolutionVisitor
let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap(); let region_expr_count = typeck_results.region_scope_tree.body_expr_count(body_id).unwrap();
assert_eq!(region_expr_count, visitor.expr_count); assert_eq!(region_expr_count, visitor.expr_count);
// The types are already kept in insertion order. // The types are already kept in insertion order.
@ -248,8 +251,9 @@ pub fn resolve_interior<'a, 'tcx>(
let witness = let witness =
fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone()));
drop(typeck_results);
// Store the generator types and spans into the typeck results for this generator. // Store the generator types and spans into the typeck results for this generator.
visitor.fcx.inh.typeck_results.borrow_mut().generator_interior_types = fcx.inh.typeck_results.borrow_mut().generator_interior_types =
ty::Binder::bind_with_vars(type_causes, bound_vars); ty::Binder::bind_with_vars(type_causes, bound_vars);
debug!( debug!(
@ -381,12 +385,14 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
// temporary on the stack that is live for the current temporary scope and then return a // temporary on the stack that is live for the current temporary scope and then return a
// reference to it. That value may be live across the entire temporary scope. // reference to it. That value may be live across the entire temporary scope.
let scope = if self.drop_ranges.is_borrowed_temporary(expr) { let scope = if self.drop_ranges.is_borrowed_temporary(expr) {
self.region_scope_tree.temporary_scope(expr.hir_id.local_id) self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
} else { } else {
debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id)); debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id));
match self.fcx.tcx.hir().find_parent_node(expr.hir_id) { match self.fcx.tcx.hir().find_parent_node(expr.hir_id) {
Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }),
None => self.region_scope_tree.temporary_scope(expr.hir_id.local_id), None => {
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id)
}
} }
}; };

View File

@ -41,11 +41,12 @@ pub fn compute_drop_ranges<'a, 'tcx>(
if fcx.sess().opts.debugging_opts.drop_tracking { if fcx.sess().opts.debugging_opts.drop_tracking {
let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); let typeck_results = &fcx.typeck_results.borrow();
let num_exprs = typeck_results.region_scope_tree.body_expr_count(body.id()).unwrap_or(0);
let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph( let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph(
fcx.tcx.hir(), fcx.tcx.hir(),
fcx.tcx, fcx.tcx,
&fcx.typeck_results.borrow(), typeck_results,
consumed_borrowed_places, consumed_borrowed_places,
body, body,
num_exprs, num_exprs,

View File

@ -85,7 +85,9 @@ pub mod method;
mod op; mod op;
mod pat; mod pat;
mod place_op; mod place_op;
mod region;
mod regionck; mod regionck;
pub mod rvalue_scopes;
mod upvar; mod upvar;
mod wfcheck; mod wfcheck;
pub mod writeback; pub mod writeback;
@ -473,6 +475,9 @@ fn typeck_with_fallback<'tcx>(
// because they don't constrain other type variables. // because they don't constrain other type variables.
fcx.closure_analyze(body); fcx.closure_analyze(body);
assert!(fcx.deferred_call_resolutions.borrow().is_empty()); assert!(fcx.deferred_call_resolutions.borrow().is_empty());
// Before the generator analysis, temporary scopes shall be marked to provide more
// precise information on types to be captured.
fcx.resolve_rvalue_scopes(def_id.to_def_id());
fcx.resolve_generator_interiors(def_id.to_def_id()); fcx.resolve_generator_interiors(def_id.to_def_id());
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {

View File

@ -14,7 +14,6 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt};
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_middle::middle::region::*; use rustc_middle::middle::region::*;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::source_map; use rustc_span::source_map;
use rustc_span::Span; use rustc_span::Span;
@ -527,7 +526,13 @@ fn resolve_local<'tcx>(
if let Some(pat) = pat { if let Some(pat) = pat {
if is_binding_pat(pat) { if is_binding_pat(pat) {
record_rvalue_scope(visitor, &expr, blk_scope); visitor.scope_tree.record_rvalue_candidate(
expr.hir_id,
RvalueCandidateType::Pattern {
target: expr.hir_id.local_id,
lifetime: blk_scope,
},
);
} }
} }
} }
@ -625,9 +630,15 @@ fn resolve_local<'tcx>(
blk_id: Option<Scope>, blk_id: Option<Scope>,
) { ) {
match expr.kind { match expr.kind {
hir::ExprKind::AddrOf(_, _, ref subexpr) => { hir::ExprKind::AddrOf(_, _, subexpr) => {
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
record_rvalue_scope(visitor, &subexpr, blk_id); visitor.scope_tree.record_rvalue_candidate(
subexpr.hir_id,
RvalueCandidateType::Borrow {
target: subexpr.hir_id.local_id,
lifetime: blk_id,
},
);
} }
hir::ExprKind::Struct(_, fields, _) => { hir::ExprKind::Struct(_, fields, _) => {
for field in fields { for field in fields {
@ -647,52 +658,15 @@ fn resolve_local<'tcx>(
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id);
} }
} }
_ => {} hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => {
} // FIXME(@dingxiangfei2009): choose call arguments here
} // for candidacy for extended parameter rule application
/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by
/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that
/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let`
/// statement.
///
/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching
/// `<rvalue>` as `blk_id`:
///
/// ```text
/// ET = *ET
/// | ET[...]
/// | ET.f
/// | (ET)
/// | <rvalue>
/// ```
///
/// Note: ET is intended to match "rvalues or places based on rvalues".
fn record_rvalue_scope<'tcx>(
visitor: &mut RegionResolutionVisitor<'tcx>,
expr: &hir::Expr<'_>,
blk_scope: Option<Scope>,
) {
let mut expr = expr;
loop {
// Note: give all the expressions matching `ET` with the
// extended temporary lifetime, not just the innermost rvalue,
// because in codegen if we must compile e.g., `*rvalue()`
// into a temporary, we request the temporary scope of the
// outer expression.
visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope);
match expr.kind {
hir::ExprKind::AddrOf(_, _, ref subexpr)
| hir::ExprKind::Unary(hir::UnOp::Deref, ref subexpr)
| hir::ExprKind::Field(ref subexpr, _)
| hir::ExprKind::Index(ref subexpr, _) => {
expr = &subexpr;
}
_ => {
return;
}
} }
hir::ExprKind::Index(..) => {
// FIXME(@dingxiangfei2009): select the indices
// as candidate for rvalue scope rules
}
_ => {}
} }
} }
} }
@ -821,14 +795,16 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
} }
} }
fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { /// 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.
pub fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> ScopeTree {
let typeck_root_def_id = tcx.typeck_root_def_id(def_id); let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
if typeck_root_def_id != def_id { if typeck_root_def_id != def_id {
return tcx.region_scope_tree(typeck_root_def_id); return region_scope_tree(tcx, typeck_root_def_id);
} }
let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
let scope_tree = if let Some(body_id) = tcx.hir().maybe_body_owned_by(id) { if let Some(body_id) = tcx.hir().maybe_body_owned_by(id) {
let mut visitor = RegionResolutionVisitor { let mut visitor = RegionResolutionVisitor {
tcx, tcx,
scope_tree: ScopeTree::default(), scope_tree: ScopeTree::default(),
@ -845,11 +821,5 @@ fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree {
visitor.scope_tree visitor.scope_tree
} else { } else {
ScopeTree::default() ScopeTree::default()
}; }
tcx.arena.alloc(scope_tree)
}
pub fn provide(providers: &mut Providers) {
*providers = Providers { region_scope_tree, ..*providers };
} }

View File

@ -75,7 +75,6 @@
use crate::check::dropck; use crate::check::dropck;
use crate::check::FnCtxt; use crate::check::FnCtxt;
use crate::mem_categorization as mc; use crate::mem_categorization as mc;
use crate::middle::region;
use crate::outlives::outlives_bounds::InferCtxtExt as _; use crate::outlives::outlives_bounds::InferCtxtExt as _;
use rustc_data_structures::stable_set::FxHashSet; use rustc_data_structures::stable_set::FxHashSet;
use rustc_hir as hir; use rustc_hir as hir;
@ -219,8 +218,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub struct RegionCtxt<'a, 'tcx> { pub struct RegionCtxt<'a, 'tcx> {
pub fcx: &'a FnCtxt<'a, 'tcx>, pub fcx: &'a FnCtxt<'a, 'tcx>,
pub region_scope_tree: &'tcx region::ScopeTree,
outlives_environment: OutlivesEnvironment<'tcx>, outlives_environment: OutlivesEnvironment<'tcx>,
// id of innermost fn body id // id of innermost fn body id
@ -247,11 +244,9 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
Subject(subject): Subject, Subject(subject): Subject,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
) -> RegionCtxt<'a, 'tcx> { ) -> RegionCtxt<'a, 'tcx> {
let region_scope_tree = fcx.tcx.region_scope_tree(subject);
let outlives_environment = OutlivesEnvironment::new(param_env); let outlives_environment = OutlivesEnvironment::new(param_env);
RegionCtxt { RegionCtxt {
fcx, fcx,
region_scope_tree,
body_id: initial_body_id, body_id: initial_body_id,
body_owner: subject, body_owner: subject,
subject_def_id: subject, subject_def_id: subject,

View File

@ -0,0 +1,83 @@
use super::FnCtxt;
use hir::def_id::DefId;
use hir::Node;
use rustc_hir as hir;
use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree};
use rustc_middle::ty::RvalueScopes;
/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by
/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that
/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let`
/// statement.
///
/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching
/// `<rvalue>` as `blk_id`:
///
/// ```text
/// ET = *ET
/// | ET[...]
/// | ET.f
/// | (ET)
/// | <rvalue>
/// ```
///
/// Note: ET is intended to match "rvalues or places based on rvalues".
fn record_rvalue_scope_rec(
rvalue_scopes: &mut RvalueScopes,
mut expr: &hir::Expr<'_>,
lifetime: Option<Scope>,
) {
loop {
// Note: give all the expressions matching `ET` with the
// extended temporary lifetime, not just the innermost rvalue,
// because in codegen if we must compile e.g., `*rvalue()`
// into a temporary, we request the temporary scope of the
// outer expression.
rvalue_scopes.record_rvalue_scope(expr.hir_id.local_id, lifetime);
match expr.kind {
hir::ExprKind::AddrOf(_, _, subexpr)
| hir::ExprKind::Unary(hir::UnOp::Deref, subexpr)
| hir::ExprKind::Field(subexpr, _)
| hir::ExprKind::Index(subexpr, _) => {
expr = subexpr;
}
_ => {
return;
}
}
}
}
fn record_rvalue_scope(
rvalue_scopes: &mut RvalueScopes,
expr: &hir::Expr<'_>,
candidate: &RvalueCandidateType,
) {
debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})");
match candidate {
RvalueCandidateType::Borrow { lifetime, .. }
| RvalueCandidateType::Pattern { lifetime, .. } => {
record_rvalue_scope_rec(rvalue_scopes, expr, *lifetime)
} // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments
}
}
pub fn resolve_rvalue_scopes<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
scope_tree: &'a ScopeTree,
def_id: DefId,
) -> RvalueScopes {
let tcx = &fcx.tcx;
let hir_map = tcx.hir();
let mut rvalue_scopes = RvalueScopes::new();
debug!("start resolving rvalue scopes, def_id={def_id:?}");
debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates);
for (&hir_id, candidate) in &scope_tree.rvalue_candidates {
let Some(Node::Expr(expr)) = hir_map.find(hir_id) else {
bug!("hir node does not exist")
};
record_rvalue_scope(&mut rvalue_scopes, expr, candidate);
}
rvalue_scopes
}

View File

@ -71,6 +71,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
wbcx.visit_user_provided_sigs(); wbcx.visit_user_provided_sigs();
wbcx.visit_generator_interior_types(); wbcx.visit_generator_interior_types();
wbcx.typeck_results.region_scope_tree =
mem::take(&mut self.typeck_results.borrow_mut().region_scope_tree);
wbcx.typeck_results.rvalue_scopes =
mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes);
let used_trait_imports = let used_trait_imports =
mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports); mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports);
debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);

View File

@ -3,7 +3,9 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::has_iter_method; use clippy_utils::ty::has_iter_method;
use clippy_utils::visitors::is_local_used; use clippy_utils::visitors::is_local_used;
use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; use clippy_utils::{
contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq,
};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -27,12 +29,7 @@ pub(super) fn check<'tcx>(
body: &'tcx Expr<'_>, body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,
) { ) {
if let Some(higher::Range { if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::Range::hir(arg) {
start: Some(start),
ref end,
limits,
}) = higher::Range::hir(arg)
{
// the var must be a single name // the var must be a single name
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
let mut visitor = VarVisitor { let mut visitor = VarVisitor {
@ -58,7 +55,11 @@ pub(super) fn check<'tcx>(
// ensure that the indexed variable was declared before the loop, see #601 // ensure that the indexed variable was declared before the loop, see #601
if let Some(indexed_extent) = indexed_extent { if let Some(indexed_extent) = indexed_extent {
let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id); let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id);
let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id); let parent_body_id = cx
.tcx
.hir()
.body_owned_by(cx.tcx.hir().local_def_id_to_hir_id(parent_def_id));
let region_scope_tree = &cx.tcx.typeck_body(parent_body_id).region_scope_tree;
let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) { if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
return; return;
@ -107,17 +108,22 @@ pub(super) fn check<'tcx>(
} }
} }
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty)
{
String::new() String::new()
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { } else if visitor.indexed_mut.contains(&indexed)
&& contains_name(indexed, take_expr)
{
return; return;
} else { } else {
match limits { match limits {
ast::RangeLimits::Closed => { ast::RangeLimits::Closed => {
let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>"); let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
format!(".take({})", take_expr + sugg::ONE) format!(".take({})", take_expr + sugg::ONE)
}, }
ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, take_expr.span, "..")), ast::RangeLimits::HalfOpen => {
format!(".take({})", snippet(cx, take_expr.span, ".."))
}
} }
} }
} else { } else {
@ -143,7 +149,10 @@ pub(super) fn check<'tcx>(
cx, cx,
NEEDLESS_RANGE_LOOP, NEEDLESS_RANGE_LOOP,
arg.span, arg.span,
&format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), &format!(
"the loop variable `{}` is used to index `{}`",
ident.name, indexed
),
|diag| { |diag| {
multispan_sugg( multispan_sugg(
diag, diag,
@ -152,7 +161,10 @@ pub(super) fn check<'tcx>(
(pat.span, format!("({}, <item>)", ident.name)), (pat.span, format!("({}, <item>)", ident.name)),
( (
arg.span, arg.span,
format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), format!(
"{}.{}().enumerate(){}{}",
indexed, method, method_1, method_2
),
), ),
], ],
); );
@ -169,7 +181,10 @@ pub(super) fn check<'tcx>(
cx, cx,
NEEDLESS_RANGE_LOOP, NEEDLESS_RANGE_LOOP,
arg.span, arg.span,
&format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), &format!(
"the loop variable `{}` is only used to index `{}`",
ident.name, indexed
),
|diag| { |diag| {
multispan_sugg( multispan_sugg(
diag, diag,
@ -246,7 +261,12 @@ struct VarVisitor<'a, 'tcx> {
} }
impl<'a, 'tcx> VarVisitor<'a, 'tcx> { impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { fn check(
&mut self,
idx: &'tcx Expr<'_>,
seqexpr: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
) -> bool {
if_chain! { if_chain! {
// the indexed container is referenced by a name // the indexed container is referenced by a name
if let ExprKind::Path(ref seqpath) = seqexpr.kind; if let ExprKind::Path(ref seqpath) = seqexpr.kind;
@ -262,7 +282,16 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
match res { match res {
Res::Local(hir_id) => { Res::Local(hir_id) => {
let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id).unwrap(); let parent_body_id = self.cx
.tcx
.hir()
.body_owned_by(self.cx.tcx.hir().local_def_id_to_hir_id(parent_def_id));
let extent = self.cx
.tcx
.typeck_body(parent_body_id)
.region_scope_tree
.var_scope(hir_id.local_id)
.unwrap();
if index_used_directly { if index_used_directly {
self.indexed_directly.insert( self.indexed_directly.insert(
seqvar.segments[0].ident.name, seqvar.segments[0].ident.name,
@ -331,13 +360,13 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
self.visit_expr(lhs); self.visit_expr(lhs);
self.prefer_mutable = false; self.prefer_mutable = false;
self.visit_expr(rhs); self.visit_expr(rhs);
}, }
ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
if mutbl == Mutability::Mut { if mutbl == Mutability::Mut {
self.prefer_mutable = true; self.prefer_mutable = true;
} }
self.visit_expr(expr); self.visit_expr(expr);
}, }
ExprKind::Call(f, args) => { ExprKind::Call(f, args) => {
self.visit_expr(f); self.visit_expr(f);
for expr in args { for expr in args {
@ -350,10 +379,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
} }
self.visit_expr(expr); self.visit_expr(expr);
} }
}, }
ExprKind::MethodCall(_, args, _) => { ExprKind::MethodCall(_, args, _) => {
let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) { for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args)
{
self.prefer_mutable = false; self.prefer_mutable = false;
if let ty::Ref(_, _, mutbl) = *ty.kind() { if let ty::Ref(_, _, mutbl) = *ty.kind() {
if mutbl == Mutability::Mut { if mutbl == Mutability::Mut {
@ -362,11 +392,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
} }
self.visit_expr(expr); self.visit_expr(expr);
} }
}, }
ExprKind::Closure(_, _, body_id, ..) => { ExprKind::Closure(_, _, body_id, ..) => {
let body = self.cx.tcx.hir().body(body_id); let body = self.cx.tcx.hir().body(body_id);
self.visit_expr(&body.value); self.visit_expr(&body.value);
}, }
_ => walk_expr(self, expr), _ => walk_expr(self, expr),
} }
self.prefer_mutable = old; self.prefer_mutable = old;

View File

@ -5,7 +5,9 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_hir::hir_id::ItemLocalId; use rustc_hir::hir_id::ItemLocalId;
use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp}; use rustc_hir::{
Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp,
};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
@ -139,27 +141,31 @@ impl<'tcx> LateLintPass<'tcx> for Shadow {
fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
let hir = cx.tcx.hir(); let hir = cx.tcx.hir();
if !matches!( if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure)
hir.body_owner_kind(hir.body_owner_def_id(body.id())), {
BodyOwnerKind::Closure
) {
self.bindings.push(FxHashMap::default()); self.bindings.push(FxHashMap::default());
} }
} }
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
let hir = cx.tcx.hir(); let hir = cx.tcx.hir();
if !matches!( if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure)
hir.body_owner_kind(hir.body_owner_def_id(body.id())), {
BodyOwnerKind::Closure
) {
self.bindings.pop(); self.bindings.pop();
} }
} }
} }
fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { fn is_shadow(
let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); cx: &LateContext<'_>,
owner: LocalDefId,
first: ItemLocalId,
second: ItemLocalId,
) -> bool {
let scope_tree = &cx
.tcx
.typeck_body(cx.tcx.hir().body_owned_by(cx.tcx.hir().local_def_id_to_hir_id(owner)))
.region_scope_tree;
let first_scope = scope_tree.var_scope(first).unwrap(); let first_scope = scope_tree.var_scope(first).unwrap();
let second_scope = scope_tree.var_scope(second).unwrap(); let second_scope = scope_tree.var_scope(second).unwrap();
scope_tree.is_subscope_of(second_scope, first_scope) scope_tree.is_subscope_of(second_scope, first_scope)
@ -174,15 +180,16 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span)
snippet(cx, expr.span, "..") snippet(cx, expr.span, "..")
); );
(SHADOW_SAME, msg) (SHADOW_SAME, msg)
}, }
Some(expr) if is_local_used(cx, expr, shadowed) => { Some(expr) if is_local_used(cx, expr, shadowed) => {
let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_"));
(SHADOW_REUSE, msg) (SHADOW_REUSE, msg)
}, }
_ => { _ => {
let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); let msg =
format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
(SHADOW_UNRELATED, msg) (SHADOW_UNRELATED, msg)
}, }
}; };
span_lint_and_note( span_lint_and_note(
cx, cx,
@ -211,14 +218,7 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_
expr = match expr.kind { expr = match expr.kind {
ExprKind::Box(e) ExprKind::Box(e)
| ExprKind::AddrOf(_, _, e) | ExprKind::AddrOf(_, _, e)
| ExprKind::Block( | ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _)
&Block {
stmts: [],
expr: Some(e),
..
},
_,
)
| ExprKind::Unary(UnOp::Deref, e) => e, | ExprKind::Unary(UnOp::Deref, e) => e,
ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id), ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id),
_ => break false, _ => break false,