Initial work for doing minimum capture analysis for RFC-2229
Co-authored-by: Chris Pardy <chrispardy36@gmail.com> Co-authored-by: Logan Mosier <logmosier@gmail.com>
This commit is contained in:
parent
145312075f
commit
8f0c0d656d
5 changed files with 374 additions and 115 deletions
|
@ -415,9 +415,10 @@ pub struct TypeckResults<'tcx> {
|
||||||
/// entire variable.
|
/// entire variable.
|
||||||
pub closure_captures: ty::UpvarListMap,
|
pub closure_captures: ty::UpvarListMap,
|
||||||
|
|
||||||
/// Given the closure ID this map provides the list of
|
/// Given the closure DefId this map provides a map of
|
||||||
/// `Place`s and how/why are they captured by the closure.
|
/// root variables to minimum set of `Place`s (and how) that need to be tracked
|
||||||
pub closure_capture_information: ty::CaptureInformationMap<'tcx>,
|
/// to support all captures of that closure.
|
||||||
|
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
|
||||||
|
|
||||||
/// 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).
|
||||||
|
@ -446,7 +447,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||||
tainted_by_errors: None,
|
tainted_by_errors: None,
|
||||||
concrete_opaque_types: Default::default(),
|
concrete_opaque_types: Default::default(),
|
||||||
closure_captures: Default::default(),
|
closure_captures: Default::default(),
|
||||||
closure_capture_information: Default::default(),
|
closure_min_captures: Default::default(),
|
||||||
generator_interior_types: Default::default(),
|
generator_interior_types: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -681,7 +682,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
|
||||||
tainted_by_errors,
|
tainted_by_errors,
|
||||||
ref concrete_opaque_types,
|
ref concrete_opaque_types,
|
||||||
ref closure_captures,
|
ref closure_captures,
|
||||||
ref closure_capture_information,
|
ref closure_min_captures,
|
||||||
ref generator_interior_types,
|
ref generator_interior_types,
|
||||||
} = *self;
|
} = *self;
|
||||||
|
|
||||||
|
@ -715,7 +716,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
|
||||||
tainted_by_errors.hash_stable(hcx, hasher);
|
tainted_by_errors.hash_stable(hcx, hasher);
|
||||||
concrete_opaque_types.hash_stable(hcx, hasher);
|
concrete_opaque_types.hash_stable(hcx, hasher);
|
||||||
closure_captures.hash_stable(hcx, hasher);
|
closure_captures.hash_stable(hcx, hasher);
|
||||||
closure_capture_information.hash_stable(hcx, hasher);
|
closure_min_captures.hash_stable(hcx, hasher);
|
||||||
generator_interior_types.hash_stable(hcx, hasher);
|
generator_interior_types.hash_stable(hcx, hasher);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -788,30 +788,18 @@ pub struct CaptureInfo<'tcx> {
|
||||||
pub capture_kind: UpvarCapture<'tcx>,
|
pub capture_kind: UpvarCapture<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||||
|
pub struct CapturedPlace<'tcx> {
|
||||||
|
pub place: HirPlace<'tcx>,
|
||||||
|
pub info: CaptureInfo<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
|
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
|
||||||
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;
|
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;
|
||||||
|
|
||||||
/// Consider closure where s.str1 is captured via an ImmutableBorrow and s.str2 via a MutableBorrow
|
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
|
||||||
///
|
pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureList<'tcx>>;
|
||||||
/// ```rust
|
pub type MinCaptureInformationMap<'tcx> = FxHashMap<DefId, RootVariableMinCaptureList<'tcx>>;
|
||||||
/// // Assume that thte HirId for the variable definition is `V1`
|
|
||||||
/// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") }
|
|
||||||
///
|
|
||||||
/// let fix_s = |new_s2| {
|
|
||||||
/// // Assume that the HirId for the expression `s.str1` is `E1`
|
|
||||||
/// println!("Updating SomeStruct with str1=", s.str1);
|
|
||||||
/// // Assume that the HirId for the expression `*s.str2` is `E2`
|
|
||||||
/// s.str2 = new_s2;
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// For closure `fix_s`, (at a high level) the IndexMap will contain:
|
|
||||||
///
|
|
||||||
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
|
|
||||||
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
|
|
||||||
///
|
|
||||||
pub type CaptureInformationMap<'tcx> =
|
|
||||||
FxHashMap<DefId, FxIndexMap<HirPlace<'tcx>, CaptureInfo<'tcx>>>;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum IntVarValue {
|
pub enum IntVarValue {
|
||||||
|
|
|
@ -383,14 +383,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
self.describe_field_from_ty(&ty, field, variant_index)
|
self.describe_field_from_ty(&ty, field, variant_index)
|
||||||
}
|
}
|
||||||
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
|
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
|
||||||
// `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case
|
// We won't be borrowck'ing here if the closure came from another crate,
|
||||||
// the closure comes from another crate. But in that case we wouldn't
|
// so it's safe to call `expect_local`.
|
||||||
// be borrowck'ing it, so we can just unwrap:
|
//
|
||||||
let (&var_id, _) = self
|
// We know the field exists so it's safe to call operator[] and `unwrap` here.
|
||||||
.infcx
|
let (&var_id, _) =
|
||||||
.tcx
|
self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id]
|
||||||
.upvars_mentioned(def_id)
|
|
||||||
.unwrap()
|
|
||||||
.get_index(field.index())
|
.get_index(field.index())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -967,9 +965,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
|
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
|
||||||
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
|
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
|
||||||
if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
|
if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
|
||||||
for ((upvar_hir_id, upvar), place) in
|
for (upvar_hir_id, place) in
|
||||||
self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places)
|
self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id]
|
||||||
|
.keys()
|
||||||
|
.zip(places)
|
||||||
{
|
{
|
||||||
|
let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span;
|
||||||
match place {
|
match place {
|
||||||
Operand::Copy(place) | Operand::Move(place)
|
Operand::Copy(place) | Operand::Move(place)
|
||||||
if target_place == place.as_ref() =>
|
if target_place == place.as_ref() =>
|
||||||
|
@ -991,7 +992,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
let usage_span =
|
let usage_span =
|
||||||
match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) {
|
match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) {
|
||||||
ty::UpvarCapture::ByValue(Some(span)) => span,
|
ty::UpvarCapture::ByValue(Some(span)) => span,
|
||||||
_ => upvar.span,
|
_ => span,
|
||||||
};
|
};
|
||||||
return Some((*args_span, generator_kind, usage_span));
|
return Some((*args_span, generator_kind, usage_span));
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,11 +39,13 @@ use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||||
use rustc_infer::infer::UpvarRegion;
|
use rustc_infer::infer::UpvarRegion;
|
||||||
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId};
|
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
|
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
macro_rules! log_capture_analysis {
|
macro_rules! log_capture_analysis {
|
||||||
($fcx:expr, $closure_def_id:expr, $fmt:literal) => {
|
($fcx:expr, $closure_def_id:expr, $fmt:literal) => {
|
||||||
if $fcx.should_log_capture_analysis($closure_def_id) {
|
if $fcx.should_log_capture_analysis($closure_def_id) {
|
||||||
|
@ -60,6 +62,17 @@ macro_rules! log_capture_analysis {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describe the relationship between the paths of two places
|
||||||
|
/// eg:
|
||||||
|
/// - foo is ancestor of foo.bar.baz
|
||||||
|
/// - foo.bar.baz is an descendant of foo.bar,
|
||||||
|
/// - foo.bar and foo.baz are divergent
|
||||||
|
enum PlaceAncestryRelation {
|
||||||
|
Ancestor,
|
||||||
|
Descendant,
|
||||||
|
Divergent,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
|
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
|
||||||
InferBorrowKindVisitor { fcx: self }.visit_body(body);
|
InferBorrowKindVisitor { fcx: self }.visit_body(body);
|
||||||
|
@ -130,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let local_def_id = closure_def_id.expect_local();
|
let local_def_id = closure_def_id.expect_local();
|
||||||
|
|
||||||
let mut capture_information = FxIndexMap::<Place<'tcx>, ty::CaptureInfo<'tcx>>::default();
|
let mut capture_information = FxIndexMap::<Place<'tcx>, ty::CaptureInfo<'tcx>>::default();
|
||||||
if self.tcx.features().capture_disjoint_fields {
|
if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) {
|
||||||
log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis");
|
log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis");
|
||||||
} else {
|
} else {
|
||||||
log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis");
|
log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis");
|
||||||
|
@ -192,12 +205,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_closure_captures(closure_def_id, &delegate);
|
self.compute_min_captures(closure_def_id, delegate);
|
||||||
|
self.set_closure_captures(closure_def_id);
|
||||||
self.typeck_results
|
|
||||||
.borrow_mut()
|
|
||||||
.closure_capture_information
|
|
||||||
.insert(closure_def_id, delegate.capture_information);
|
|
||||||
|
|
||||||
// Now that we've analyzed the closure, we know how each
|
// Now that we've analyzed the closure, we know how each
|
||||||
// variable is borrowed, and we know what traits the closure
|
// variable is borrowed, and we know what traits the closure
|
||||||
|
@ -266,43 +275,221 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_closure_captures(
|
/// Bridge for closure analysis
|
||||||
&self,
|
/// ----------------------------
|
||||||
closure_def_id: DefId,
|
///
|
||||||
inferred_info: &InferBorrowKind<'_, 'tcx>,
|
/// For closure with DefId `c`, the bridge converts structures required for supporting RFC 2229,
|
||||||
) {
|
/// to structures currently used in the compiler for handling closure captures.
|
||||||
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> = Default::default();
|
///
|
||||||
|
/// For example the following structure will be converted:
|
||||||
|
///
|
||||||
|
/// closure_min_captures
|
||||||
|
/// foo -> [ {foo.x, ImmBorrow}, {foo.y, MutBorrow} ]
|
||||||
|
/// bar -> [ {bar.z, ByValue}, {bar.q, MutBorrow} ]
|
||||||
|
///
|
||||||
|
/// to
|
||||||
|
///
|
||||||
|
/// 1. closure_captures
|
||||||
|
/// foo -> UpvarId(foo, c), bar -> UpvarId(bar, c)
|
||||||
|
///
|
||||||
|
/// 2. upvar_capture_map
|
||||||
|
/// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue
|
||||||
|
|
||||||
|
fn set_closure_captures(&self, closure_def_id: DefId) {
|
||||||
|
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> = Default::default();
|
||||||
|
let mut upvar_capture_map = ty::UpvarCaptureMap::default();
|
||||||
|
|
||||||
|
if let Some(min_captures) =
|
||||||
|
self.typeck_results.borrow().closure_min_captures.get(&closure_def_id)
|
||||||
|
{
|
||||||
|
for (var_hir_id, min_list) in min_captures.iter() {
|
||||||
|
for captured_place in min_list {
|
||||||
|
let place = &captured_place.place;
|
||||||
|
let capture_info = captured_place.info;
|
||||||
|
|
||||||
for (place, capture_info) in inferred_info.capture_information.iter() {
|
|
||||||
let upvar_id = match place.base {
|
let upvar_id = match place.base {
|
||||||
PlaceBase::Upvar(upvar_id) => upvar_id,
|
PlaceBase::Upvar(upvar_id) => upvar_id,
|
||||||
base => bug!("Expected upvar, found={:?}", base),
|
base => bug!("Expected upvar, found={:?}", base),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert_eq!(upvar_id.var_path.hir_id, *var_hir_id);
|
||||||
assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local());
|
assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local());
|
||||||
|
|
||||||
let var_hir_id = upvar_id.var_path.hir_id;
|
closure_captures.insert(*var_hir_id, upvar_id);
|
||||||
closure_captures.insert(var_hir_id, upvar_id);
|
|
||||||
|
|
||||||
let new_capture_kind = if let Some(capture_kind) =
|
let new_capture_kind = if let Some(capture_kind) =
|
||||||
self.typeck_results.borrow_mut().upvar_capture_map.get(&upvar_id)
|
upvar_capture_map.get(&upvar_id)
|
||||||
{
|
{
|
||||||
// upvar_capture_map only stores the UpvarCapture (CaptureKind),
|
// upvar_capture_map only stores the UpvarCapture (CaptureKind),
|
||||||
// so we create a fake capture info with no expression.
|
// so we create a fake capture info with no expression.
|
||||||
let fake_capture_info =
|
let fake_capture_info =
|
||||||
ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() };
|
ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() };
|
||||||
self.determine_capture_info(fake_capture_info, capture_info.clone()).capture_kind
|
self.determine_capture_info(fake_capture_info, capture_info.clone())
|
||||||
|
.capture_kind
|
||||||
} else {
|
} else {
|
||||||
capture_info.capture_kind
|
capture_info.capture_kind
|
||||||
};
|
};
|
||||||
self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind);
|
upvar_capture_map.insert(upvar_id, new_capture_kind);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!(
|
||||||
|
"For closure_def_id={:?}, set_closure_captures={:#?}",
|
||||||
|
closure_def_id, closure_captures
|
||||||
|
);
|
||||||
|
debug!(
|
||||||
|
"For closure_def_id={:?}, upvar_capture_map={:#?}",
|
||||||
|
closure_def_id, upvar_capture_map
|
||||||
|
);
|
||||||
|
|
||||||
if !closure_captures.is_empty() {
|
if !closure_captures.is_empty() {
|
||||||
self.typeck_results
|
self.typeck_results
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.closure_captures
|
.closure_captures
|
||||||
.insert(closure_def_id, closure_captures);
|
.insert(closure_def_id, closure_captures);
|
||||||
|
|
||||||
|
self.typeck_results.borrow_mut().upvar_capture_map.extend(upvar_capture_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Analyses the information collected by InferBorrowKind to compute the min number of
|
||||||
|
/// Places (and corresponding capture kind) that we need to keep track of to support all
|
||||||
|
/// the required captured paths.
|
||||||
|
///
|
||||||
|
/// Eg:
|
||||||
|
/// ```rust
|
||||||
|
/// struct Point { x: i32, y: i32 }
|
||||||
|
///
|
||||||
|
/// let s: String; // hir_id_s
|
||||||
|
/// let mut p: Point; // his_id_p
|
||||||
|
/// let c = || {
|
||||||
|
/// println!("{}", s); // L1
|
||||||
|
/// p.x += 10; // L2
|
||||||
|
/// println!("{}" , p.y) // L3
|
||||||
|
/// println!("{}", p) // L4
|
||||||
|
/// drop(s); // L5
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
/// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on
|
||||||
|
/// the lines L1..5 respectively.
|
||||||
|
///
|
||||||
|
/// InferBorrowKind results in a structure like this:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// {
|
||||||
|
/// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue),
|
||||||
|
/// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow))
|
||||||
|
/// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow))
|
||||||
|
/// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow))
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// After the min capture analysis, we get:
|
||||||
|
/// ```
|
||||||
|
/// {
|
||||||
|
/// hir_id_s -> [
|
||||||
|
/// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue)
|
||||||
|
/// ],
|
||||||
|
/// hir_id_p -> [
|
||||||
|
/// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)),
|
||||||
|
/// ],
|
||||||
|
/// ```
|
||||||
|
fn compute_min_captures(
|
||||||
|
&self,
|
||||||
|
closure_def_id: DefId,
|
||||||
|
inferred_info: InferBorrowKind<'_, 'tcx>,
|
||||||
|
) {
|
||||||
|
let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default();
|
||||||
|
|
||||||
|
for (place, capture_info) in inferred_info.capture_information.into_iter() {
|
||||||
|
let var_hir_id = match place.base {
|
||||||
|
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
|
||||||
|
base => bug!("Expected upvar, found={:?}", base),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Arrays are captured in entirety, drop Index projections and projections
|
||||||
|
// after Index projections.
|
||||||
|
let first_index_projection =
|
||||||
|
place.projections.split(|proj| ProjectionKind::Index == proj.kind).next();
|
||||||
|
let place = Place {
|
||||||
|
base_ty: place.base_ty,
|
||||||
|
base: place.base,
|
||||||
|
projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
|
||||||
|
None => {
|
||||||
|
let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }];
|
||||||
|
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(min_cap_list) => min_cap_list,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Go through each entry in the current list of min_captures
|
||||||
|
// - if ancestor is found, update it's capture kind to account for current place's
|
||||||
|
// capture information.
|
||||||
|
//
|
||||||
|
// - if descendant is found, remove it from the list, and update the current place's
|
||||||
|
// capture information to account for the descendants's capture kind.
|
||||||
|
//
|
||||||
|
// We can never be in a case where the list contains both an ancestor and a descendant
|
||||||
|
// Also there can only be ancestor but in case of descendants there might be
|
||||||
|
// multiple.
|
||||||
|
|
||||||
|
let mut descendant_found = false;
|
||||||
|
let mut updated_capture_info = capture_info;
|
||||||
|
min_cap_list.retain(|possible_descendant| {
|
||||||
|
match determine_place_ancestry_relation(&place, &possible_descendant.place) {
|
||||||
|
// current place is ancestor of possible_descendant
|
||||||
|
PlaceAncestryRelation::Ancestor => {
|
||||||
|
descendant_found = true;
|
||||||
|
updated_capture_info = self
|
||||||
|
.determine_capture_info(updated_capture_info, possible_descendant.info);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut ancestor_found = false;
|
||||||
|
if !descendant_found {
|
||||||
|
for possible_ancestor in min_cap_list.iter_mut() {
|
||||||
|
match determine_place_ancestry_relation(&place, &possible_ancestor.place) {
|
||||||
|
// current place is descendant of possible_ancestor
|
||||||
|
PlaceAncestryRelation::Descendant => {
|
||||||
|
ancestor_found = true;
|
||||||
|
possible_ancestor.info =
|
||||||
|
self.determine_capture_info(possible_ancestor.info, capture_info);
|
||||||
|
|
||||||
|
// Only one ancestor of the current place will be in the list.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only need to insert when we don't have an ancestor in the existing min capture list
|
||||||
|
if !ancestor_found {
|
||||||
|
let captured_place =
|
||||||
|
ty::CapturedPlace { place: place.clone(), info: updated_capture_info };
|
||||||
|
min_cap_list.push(captured_place);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_capture_analysis!(
|
||||||
|
self,
|
||||||
|
closure_def_id,
|
||||||
|
"min_captures={:#?}",
|
||||||
|
root_var_min_capture_list
|
||||||
|
);
|
||||||
|
|
||||||
|
if !root_var_min_capture_list.is_empty() {
|
||||||
|
self.typeck_results
|
||||||
|
.borrow_mut()
|
||||||
|
.closure_min_captures
|
||||||
|
.insert(closure_def_id, root_var_min_capture_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,8 +605,27 @@ struct InferBorrowKind<'a, 'tcx> {
|
||||||
// variable access that caused us to do so.
|
// variable access that caused us to do so.
|
||||||
current_origin: Option<(Span, Symbol)>,
|
current_origin: Option<(Span, Symbol)>,
|
||||||
|
|
||||||
// For each upvar that we access, we track the minimal kind of
|
/// For each Place that we access, we track the minimal kind of
|
||||||
// access we need (ref, ref mut, move, etc).
|
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
|
||||||
|
/// Consider closure where s.str1 is captured via an ImmutableBorrow and
|
||||||
|
/// s.str2 via a MutableBorrow
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// // Assume that the HirId for the variable definition is `V1`
|
||||||
|
/// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") }
|
||||||
|
///
|
||||||
|
/// let fix_s = |new_s2| {
|
||||||
|
/// // Assume that the HirId for the expression `s.str1` is `E1`
|
||||||
|
/// println!("Updating SomeStruct with str1=", s.str1);
|
||||||
|
/// // Assume that the HirId for the expression `*s.str2` is `E2`
|
||||||
|
/// s.str2 = new_s2;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For closure `fix_s`, (at a high level) the map contains
|
||||||
|
///
|
||||||
|
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
|
||||||
|
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
|
||||||
capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
|
capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,3 +920,45 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||||
fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
|
fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
|
||||||
tcx.hir().name(var_hir_id)
|
tcx.hir().name(var_hir_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines the Ancestry relationship of Place A relative to Place B
|
||||||
|
///
|
||||||
|
/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B
|
||||||
|
/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B
|
||||||
|
/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other.
|
||||||
|
fn determine_place_ancestry_relation(
|
||||||
|
place_a: &Place<'tcx>,
|
||||||
|
place_b: &Place<'tcx>,
|
||||||
|
) -> PlaceAncestryRelation {
|
||||||
|
// If Place A and Place B, don't start off from the same root variable, they are divergent.
|
||||||
|
if place_a.base != place_b.base {
|
||||||
|
return PlaceAncestryRelation::Divergent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume of length of projections_a = n
|
||||||
|
let projections_a = &place_a.projections;
|
||||||
|
|
||||||
|
// Assume of length of projections_b = m
|
||||||
|
let projections_b = &place_b.projections;
|
||||||
|
|
||||||
|
let mut same_initial_projections = true;
|
||||||
|
|
||||||
|
for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) {
|
||||||
|
if proj_a != proj_b {
|
||||||
|
same_initial_projections = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if same_initial_projections {
|
||||||
|
// First min(n, m) projections are the same
|
||||||
|
// Select Ancestor/Descendant
|
||||||
|
if projections_b.len() >= projections_a.len() {
|
||||||
|
PlaceAncestryRelation::Ancestor
|
||||||
|
} else {
|
||||||
|
PlaceAncestryRelation::Descendant
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PlaceAncestryRelation::Divergent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ use rustc_middle::ty::{self, adjustment, TyCtxt};
|
||||||
use rustc_target::abi::VariantIdx;
|
use rustc_target::abi::VariantIdx;
|
||||||
|
|
||||||
use crate::mem_categorization as mc;
|
use crate::mem_categorization as mc;
|
||||||
use rustc_span::Span;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// The Delegate trait
|
// The Delegate trait
|
||||||
|
@ -331,8 +330,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
self.consume_expr(base);
|
self.consume_expr(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprKind::Closure(_, _, _, fn_decl_span, _) => {
|
hir::ExprKind::Closure(..) => {
|
||||||
self.walk_captures(expr, fn_decl_span);
|
self.walk_captures(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprKind::Box(ref base) => {
|
hir::ExprKind::Box(ref base) => {
|
||||||
|
@ -571,34 +570,55 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(arora-aman): fix the fn_decl_span
|
/// Handle the case where the current body contains a closure.
|
||||||
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, _fn_decl_span: Span) {
|
///
|
||||||
|
/// When the current body being handled is a closure, then we must make sure that
|
||||||
|
/// - The parent closure only captures Places from the nested closure that are not local to it.
|
||||||
|
///
|
||||||
|
/// In the following example the closures `c` only captures `p.x`` even though `incr`
|
||||||
|
/// is a capture of the nested closure
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let p = ..;
|
||||||
|
/// let c = || {
|
||||||
|
/// let incr = 10;
|
||||||
|
/// let nested = || p.x += incr;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
|
||||||
|
/// closure as the DefId.
|
||||||
|
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) {
|
||||||
debug!("walk_captures({:?})", closure_expr);
|
debug!("walk_captures({:?})", closure_expr);
|
||||||
|
|
||||||
// We are currently walking a closure that is within a given body
|
|
||||||
// We need to process all the captures for this closure.
|
|
||||||
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
|
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id();
|
||||||
let upvars = self.tcx().upvars_mentioned(self.body_owner);
|
let upvars = self.tcx().upvars_mentioned(self.body_owner);
|
||||||
if let Some(closure_capture_information) =
|
|
||||||
self.mc.typeck_results.closure_capture_information.get(&closure_def_id)
|
// For purposes of this function, generator and closures are equivalent.
|
||||||
{
|
let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() {
|
||||||
for (place, capture_info) in closure_capture_information.iter() {
|
ty::Closure(..) | ty::Generator(..) => true,
|
||||||
let var_hir_id = if let PlaceBase::Upvar(upvar_id) = place.base {
|
_ => false,
|
||||||
upvar_id.var_path.hir_id
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
// FIXME(arora-aman): throw err?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
|
if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id)
|
||||||
// The nested closure might be capturing our local variables
|
{
|
||||||
// Since for the current body these aren't captures, we will ignore them.
|
for (var_hir_id, min_list) in min_captures.iter() {
|
||||||
|
if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) {
|
||||||
|
// The nested closure might be capturing the current (enclosing) closure's local variables.
|
||||||
|
// We check if the root variable is ever mentioned within the enclosing closure, if not
|
||||||
|
// then for the current body (if it's a closure) these aren't captures, we will ignore them.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
for captured_place in min_list {
|
||||||
|
let place = &captured_place.place;
|
||||||
|
let capture_info = captured_place.info;
|
||||||
|
|
||||||
// The place is being captured by the enclosing closure
|
let upvar_id = if body_owner_is_closure {
|
||||||
// FIXME(arora-aman) Make sure this is valid to do when called from clippy.
|
// Mark the place to be captured by the enclosing closure
|
||||||
let upvar_id = ty::UpvarId::new(var_hir_id, self.body_owner);
|
ty::UpvarId::new(*var_hir_id, self.body_owner)
|
||||||
|
} else {
|
||||||
|
ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local())
|
||||||
|
};
|
||||||
let place_with_id = PlaceWithHirId::new(
|
let place_with_id = PlaceWithHirId::new(
|
||||||
capture_info.expr_id.unwrap_or(closure_expr.hir_id),
|
capture_info.expr_id.unwrap_or(closure_expr.hir_id),
|
||||||
place.base_ty,
|
place.base_ty,
|
||||||
|
@ -622,6 +642,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn copy_or_move<'a, 'tcx>(
|
fn copy_or_move<'a, 'tcx>(
|
||||||
mc: &mc::MemCategorizationContext<'a, 'tcx>,
|
mc: &mc::MemCategorizationContext<'a, 'tcx>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue