mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 05:26:47 +00:00
Use Places to express closure/generator Captures
Co-authored-by: Archer Zhang <archer.xn@gmail.com>
This commit is contained in:
parent
cf9cf7c923
commit
127a6ede1d
@ -415,6 +415,10 @@ pub struct TypeckResults<'tcx> {
|
||||
/// entire variable.
|
||||
pub closure_captures: ty::UpvarListMap,
|
||||
|
||||
/// Given the closure ID this map provides the list of
|
||||
/// `Place`s and how/why are they captured by the closure.
|
||||
pub closure_capture_information: ty::CaptureInformationMap<'tcx>,
|
||||
|
||||
/// 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: Vec<GeneratorInteriorTypeCause<'tcx>>,
|
||||
@ -442,6 +446,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||
tainted_by_errors: None,
|
||||
concrete_opaque_types: Default::default(),
|
||||
closure_captures: Default::default(),
|
||||
closure_capture_information: Default::default(),
|
||||
generator_interior_types: Default::default(),
|
||||
}
|
||||
}
|
||||
@ -676,6 +681,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
|
||||
tainted_by_errors,
|
||||
ref concrete_opaque_types,
|
||||
ref closure_captures,
|
||||
ref closure_capture_information,
|
||||
ref generator_interior_types,
|
||||
} = *self;
|
||||
|
||||
@ -709,6 +715,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
|
||||
tainted_by_errors.hash_stable(hcx, hasher);
|
||||
concrete_opaque_types.hash_stable(hcx, hasher);
|
||||
closure_captures.hash_stable(hcx, hasher);
|
||||
closure_capture_information.hash_stable(hcx, hasher);
|
||||
generator_interior_types.hash_stable(hcx, hasher);
|
||||
})
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ pub use self::IntVarValue::*;
|
||||
pub use self::Variance::*;
|
||||
|
||||
use crate::hir::exports::ExportMap;
|
||||
use crate::hir::place::Place as HirPlace;
|
||||
use crate::ich::StableHashingContext;
|
||||
use crate::middle::cstore::CrateStoreDyn;
|
||||
use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
|
||||
@ -674,6 +675,12 @@ pub struct UpvarId {
|
||||
pub closure_expr_id: LocalDefId,
|
||||
}
|
||||
|
||||
impl UpvarId {
|
||||
pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId {
|
||||
UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)]
|
||||
pub enum BorrowKind {
|
||||
/// Data must be immutable and is aliasable.
|
||||
@ -756,9 +763,40 @@ pub struct UpvarBorrow<'tcx> {
|
||||
pub region: ty::Region<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)]
|
||||
pub struct CaptureInfo<'tcx> {
|
||||
/// Expr Id pointing to use that resulting in selecting the current capture kind
|
||||
pub expr_id: Option<hir::HirId>,
|
||||
|
||||
/// Capture mode that was selected
|
||||
pub capture_kind: UpvarCapture<'tcx>,
|
||||
}
|
||||
|
||||
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
|
||||
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;
|
||||
|
||||
/// Consider closure where s.str1 is captured via an ImmutableBorrow and s.str2 via a MutableBorrow
|
||||
///
|
||||
/// ```rust
|
||||
/// // 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)]
|
||||
pub enum IntVarValue {
|
||||
IntType(ast::IntTy),
|
||||
|
@ -387,8 +387,9 @@ fn make_mirror_unadjusted<'a, 'tcx>(
|
||||
}
|
||||
};
|
||||
let upvars = cx
|
||||
.tcx
|
||||
.upvars_mentioned(def_id)
|
||||
.typeck_results()
|
||||
.closure_captures
|
||||
.get(&def_id)
|
||||
.iter()
|
||||
.flat_map(|upvars| upvars.iter())
|
||||
.zip(substs.upvar_tys())
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
use super::FnCtxt;
|
||||
|
||||
use std::env;
|
||||
|
||||
use crate::expr_use_visitor as euv;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir as hir;
|
||||
@ -39,10 +41,9 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_infer::infer::UpvarRegion;
|
||||
use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
|
||||
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
|
||||
@ -111,40 +112,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
|
||||
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> =
|
||||
FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default());
|
||||
for (&var_hir_id, _) in upvars.iter() {
|
||||
let upvar_id = ty::UpvarId {
|
||||
var_path: ty::UpvarPath { hir_id: var_hir_id },
|
||||
closure_expr_id: closure_def_id.expect_local(),
|
||||
};
|
||||
debug!("seed upvar_id {:?}", upvar_id);
|
||||
// Adding the upvar Id to the list of Upvars, which will be added
|
||||
// to the map for the closure at the end of the for loop.
|
||||
closure_captures.insert(var_hir_id, upvar_id);
|
||||
let local_def_id = closure_def_id.expect_local();
|
||||
|
||||
let capture_kind = match capture_clause {
|
||||
hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
|
||||
hir::CaptureBy::Ref => {
|
||||
let origin = UpvarRegion(upvar_id, span);
|
||||
let upvar_region = self.next_region_var(origin);
|
||||
let upvar_borrow =
|
||||
ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
|
||||
ty::UpvarCapture::ByRef(upvar_borrow)
|
||||
}
|
||||
};
|
||||
let mut capture_information = FxIndexMap::<Place<'tcx>, ty::CaptureInfo<'tcx>>::default();
|
||||
if !new_capture_analysis() {
|
||||
debug!("Using old-style capture analysis");
|
||||
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
|
||||
for (&var_hir_id, _) in upvars.iter() {
|
||||
let place = self.place_for_root_variable(local_def_id, var_hir_id);
|
||||
|
||||
self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, capture_kind);
|
||||
}
|
||||
// Add the vector of upvars to the map keyed with the closure id.
|
||||
// This gives us an easier access to them without having to call
|
||||
// tcx.upvars again..
|
||||
if !closure_captures.is_empty() {
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.closure_captures
|
||||
.insert(closure_def_id, closure_captures);
|
||||
debug!("seed place {:?}", place);
|
||||
|
||||
let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
|
||||
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
|
||||
let info = ty::CaptureInfo { expr_id: None, capture_kind };
|
||||
|
||||
capture_information.insert(place, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,9 +137,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let mut delegate = InferBorrowKind {
|
||||
fcx: self,
|
||||
closure_def_id,
|
||||
closure_span: span,
|
||||
capture_clause,
|
||||
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
|
||||
current_origin: None,
|
||||
adjust_upvar_captures: ty::UpvarCaptureMap::default(),
|
||||
capture_information,
|
||||
};
|
||||
euv::ExprUseVisitor::new(
|
||||
&mut delegate,
|
||||
@ -182,7 +168,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
self.typeck_results.borrow_mut().upvar_capture_map.extend(delegate.adjust_upvar_captures);
|
||||
self.set_closure_captures(closure_def_id, &delegate);
|
||||
|
||||
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
|
||||
// variable is borrowed, and we know what traits the closure
|
||||
@ -226,15 +217,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let closure_def_id = tcx.hir().local_def_id(closure_id);
|
||||
|
||||
tcx.upvars_mentioned(closure_def_id)
|
||||
self.typeck_results
|
||||
.borrow()
|
||||
.closure_captures
|
||||
.get(&closure_def_id.to_def_id())
|
||||
.iter()
|
||||
.flat_map(|upvars| {
|
||||
upvars.iter().map(|(&var_hir_id, _)| {
|
||||
let upvar_ty = self.node_ty(var_hir_id);
|
||||
let upvar_id = ty::UpvarId {
|
||||
var_path: ty::UpvarPath { hir_id: var_hir_id },
|
||||
closure_expr_id: closure_def_id,
|
||||
};
|
||||
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
|
||||
let capture = self.typeck_results.borrow().upvar_capture(upvar_id);
|
||||
|
||||
debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture);
|
||||
@ -250,6 +241,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn set_closure_captures(
|
||||
&self,
|
||||
closure_def_id: DefId,
|
||||
inferred_info: &InferBorrowKind<'_, 'tcx>,
|
||||
) {
|
||||
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> = Default::default();
|
||||
|
||||
for (place, capture_info) in inferred_info.capture_information.iter() {
|
||||
let upvar_id = match place.base {
|
||||
PlaceBase::Upvar(upvar_id) => upvar_id,
|
||||
base => bug!("Expected upvar, found={:?}", base),
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
let mut new_capture_kind = capture_info.capture_kind;
|
||||
if let Some(existing_capture_kind) =
|
||||
self.typeck_results.borrow_mut().upvar_capture_map.get(&upvar_id)
|
||||
{
|
||||
// FIXME(@azhng): refactor this later
|
||||
new_capture_kind = match (existing_capture_kind, new_capture_kind) {
|
||||
(ty::UpvarCapture::ByValue(Some(_)), _) => *existing_capture_kind,
|
||||
(_, ty::UpvarCapture::ByValue(Some(_))) => new_capture_kind,
|
||||
(ty::UpvarCapture::ByValue(_), _) | (_, ty::UpvarCapture::ByValue(_)) => {
|
||||
ty::UpvarCapture::ByValue(None)
|
||||
}
|
||||
(ty::UpvarCapture::ByRef(existing_ref), ty::UpvarCapture::ByRef(new_ref)) => {
|
||||
match (existing_ref.kind, new_ref.kind) {
|
||||
// Take RHS:
|
||||
(ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow)
|
||||
| (ty::UniqueImmBorrow, ty::MutBorrow) => new_capture_kind,
|
||||
// Take LHS:
|
||||
(ty::ImmBorrow, ty::ImmBorrow)
|
||||
| (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow)
|
||||
| (ty::MutBorrow, _) => *existing_capture_kind,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind);
|
||||
}
|
||||
|
||||
if !closure_captures.is_empty() {
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.closure_captures
|
||||
.insert(closure_def_id, closure_captures);
|
||||
}
|
||||
}
|
||||
|
||||
fn init_capture_kind(
|
||||
&self,
|
||||
capture_clause: hir::CaptureBy,
|
||||
upvar_id: ty::UpvarId,
|
||||
closure_span: Span,
|
||||
) -> ty::UpvarCapture<'tcx> {
|
||||
match capture_clause {
|
||||
hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
|
||||
hir::CaptureBy::Ref => {
|
||||
let origin = UpvarRegion(upvar_id, closure_span);
|
||||
let upvar_region = self.next_region_var(origin);
|
||||
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
|
||||
ty::UpvarCapture::ByRef(upvar_borrow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn place_for_root_variable(
|
||||
&self,
|
||||
closure_def_id: LocalDefId,
|
||||
var_hir_id: hir::HirId,
|
||||
) -> Place<'tcx> {
|
||||
let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id);
|
||||
|
||||
Place {
|
||||
base_ty: self.node_ty(var_hir_id),
|
||||
base: PlaceBase::Upvar(upvar_id),
|
||||
projections: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InferBorrowKind<'a, 'tcx> {
|
||||
@ -258,6 +333,10 @@ struct InferBorrowKind<'a, 'tcx> {
|
||||
// The def-id of the closure whose kind and upvar accesses are being inferred.
|
||||
closure_def_id: DefId,
|
||||
|
||||
closure_span: Span,
|
||||
|
||||
capture_clause: hir::CaptureBy,
|
||||
|
||||
// The kind that we have inferred that the current closure
|
||||
// requires. Note that we *always* infer a minimal kind, even if
|
||||
// we don't always *use* that in the final result (i.e., sometimes
|
||||
@ -272,7 +351,7 @@ struct InferBorrowKind<'a, 'tcx> {
|
||||
|
||||
// For each upvar that we access, we track the minimal kind of
|
||||
// access we need (ref, ref mut, move, etc).
|
||||
adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>,
|
||||
capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
@ -314,26 +393,21 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
|
||||
let new_capture = ty::UpvarCapture::ByValue(Some(usage_span));
|
||||
match self.adjust_upvar_captures.entry(upvar_id) {
|
||||
Entry::Occupied(mut e) => {
|
||||
match e.get() {
|
||||
// We always overwrite `ByRef`, since we require
|
||||
// that the upvar be available by value.
|
||||
//
|
||||
// If we had a previous by-value usage without a specific
|
||||
// span, use ours instead. Otherwise, keep the first span
|
||||
// we encountered, since there isn't an obviously better one.
|
||||
ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => {
|
||||
e.insert(new_capture);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(new_capture);
|
||||
}
|
||||
}
|
||||
let capture_info = ty::CaptureInfo {
|
||||
expr_id: Some(diag_expr_id),
|
||||
capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)),
|
||||
};
|
||||
|
||||
let curr_info = self.capture_information.get(&place_with_id.place);
|
||||
let updated_info = match curr_info {
|
||||
Some(info) => match info.capture_kind {
|
||||
ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => capture_info,
|
||||
_ => *info,
|
||||
},
|
||||
None => capture_info,
|
||||
};
|
||||
|
||||
self.capture_information.insert(place_with_id.place.clone(), updated_info);
|
||||
}
|
||||
|
||||
/// Indicates that `place_with_id` is being directly mutated (e.g., assigned
|
||||
@ -349,7 +423,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
place_with_id, diag_expr_id
|
||||
);
|
||||
|
||||
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
|
||||
if let PlaceBase::Upvar(_) = place_with_id.place.base {
|
||||
let mut borrow_kind = ty::MutBorrow;
|
||||
for pointer_ty in place_with_id.place.deref_tys() {
|
||||
match pointer_ty.kind() {
|
||||
@ -363,7 +437,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
self.adjust_upvar_deref(upvar_id, self.fcx.tcx.hir().span(diag_expr_id), borrow_kind);
|
||||
self.adjust_upvar_deref(place_with_id, diag_expr_id, borrow_kind);
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,24 +451,20 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
place_with_id, diag_expr_id
|
||||
);
|
||||
|
||||
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
|
||||
if let PlaceBase::Upvar(_) = place_with_id.place.base {
|
||||
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
|
||||
// Raw pointers don't inherit mutability.
|
||||
return;
|
||||
}
|
||||
// for a borrowed pointer to be unique, its base must be unique
|
||||
self.adjust_upvar_deref(
|
||||
upvar_id,
|
||||
self.fcx.tcx.hir().span(diag_expr_id),
|
||||
ty::UniqueImmBorrow,
|
||||
);
|
||||
self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow);
|
||||
}
|
||||
}
|
||||
|
||||
fn adjust_upvar_deref(
|
||||
&mut self,
|
||||
upvar_id: ty::UpvarId,
|
||||
place_span: Span,
|
||||
place_with_id: &PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: hir::HirId,
|
||||
borrow_kind: ty::BorrowKind,
|
||||
) {
|
||||
assert!(match borrow_kind {
|
||||
@ -411,15 +481,16 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
// upvar, then we need to modify the
|
||||
// borrow_kind of the upvar to make sure it
|
||||
// is inferred to mutable if necessary
|
||||
self.adjust_upvar_borrow_kind(upvar_id, borrow_kind);
|
||||
self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind);
|
||||
|
||||
// also need to be in an FnMut closure since this is not an ImmBorrow
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnMut,
|
||||
place_span,
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
|
||||
self.adjust_closure_kind(
|
||||
upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnMut,
|
||||
tcx.hir().span(diag_expr_id),
|
||||
var_name(tcx, upvar_id.var_path.hir_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// We infer the borrow_kind with which to borrow upvars in a stack closure.
|
||||
@ -427,29 +498,40 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
/// moving from left to right as needed (but never right to left).
|
||||
/// Here the argument `mutbl` is the borrow_kind that is required by
|
||||
/// some particular use.
|
||||
fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) {
|
||||
let upvar_capture = self
|
||||
.adjust_upvar_captures
|
||||
.get(&upvar_id)
|
||||
.copied()
|
||||
.unwrap_or_else(|| self.fcx.typeck_results.borrow().upvar_capture(upvar_id));
|
||||
fn adjust_upvar_borrow_kind(
|
||||
&mut self,
|
||||
place_with_id: &PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: hir::HirId,
|
||||
kind: ty::BorrowKind,
|
||||
) {
|
||||
let capture_info = self
|
||||
.capture_information
|
||||
.get(&place_with_id.place)
|
||||
.unwrap_or_else(|| bug!("Upar capture info missing"));
|
||||
// We init capture_information for each element
|
||||
|
||||
debug!(
|
||||
"adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})",
|
||||
upvar_id, upvar_capture, kind
|
||||
"adjust_upvar_borrow_kind(place={:?}, , diag_expr_id={:?}, capture_info={:?}, kind={:?})",
|
||||
place_with_id, diag_expr_id, capture_info, kind
|
||||
);
|
||||
|
||||
match upvar_capture {
|
||||
match capture_info.capture_kind {
|
||||
ty::UpvarCapture::ByValue(_) => {
|
||||
// Upvar is already by-value, the strongest criteria.
|
||||
}
|
||||
ty::UpvarCapture::ByRef(mut upvar_borrow) => {
|
||||
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
||||
match (upvar_borrow.kind, kind) {
|
||||
// Take RHS:
|
||||
(ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow)
|
||||
| (ty::UniqueImmBorrow, ty::MutBorrow) => {
|
||||
upvar_borrow.kind = kind;
|
||||
self.adjust_upvar_captures
|
||||
.insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow));
|
||||
if let Some(ty::CaptureInfo { expr_id, capture_kind }) =
|
||||
self.capture_information.get_mut(&place_with_id.place)
|
||||
{
|
||||
*expr_id = Some(diag_expr_id);
|
||||
if let ty::UpvarCapture::ByRef(borrow_kind) = capture_kind {
|
||||
borrow_kind.kind = kind;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Take LHS:
|
||||
(ty::ImmBorrow, ty::ImmBorrow)
|
||||
@ -501,6 +583,33 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_capture_info_for_place(
|
||||
&mut self,
|
||||
place_with_id: &PlaceWithHirId<'tcx>,
|
||||
diag_expr_id: hir::HirId,
|
||||
) {
|
||||
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
|
||||
assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id);
|
||||
|
||||
debug!("Capturing new place {:?}", place_with_id);
|
||||
|
||||
let tcx = self.fcx.tcx;
|
||||
let capture_kind =
|
||||
self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span);
|
||||
|
||||
let expr_id = Some(diag_expr_id);
|
||||
let capture_info = ty::CaptureInfo { expr_id, capture_kind };
|
||||
|
||||
if log_capture_analysis() {
|
||||
debug!("capture_info: {:?}", capture_info);
|
||||
}
|
||||
|
||||
self.capture_information.insert(place_with_id.place.clone(), capture_info);
|
||||
} else {
|
||||
debug!("Not upvar: {:?}", place_with_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
@ -514,7 +623,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
"consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
|
||||
place_with_id, diag_expr_id, mode
|
||||
);
|
||||
self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode);
|
||||
if !self.capture_information.contains_key(&place_with_id.place) {
|
||||
self.init_capture_info_for_place(place_with_id, diag_expr_id);
|
||||
}
|
||||
|
||||
self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode);
|
||||
}
|
||||
|
||||
fn borrow(
|
||||
@ -528,6 +641,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
place_with_id, diag_expr_id, bk
|
||||
);
|
||||
|
||||
if !self.capture_information.contains_key(&place_with_id.place) {
|
||||
self.init_capture_info_for_place(place_with_id, diag_expr_id);
|
||||
}
|
||||
|
||||
match bk {
|
||||
ty::ImmBorrow => {}
|
||||
ty::UniqueImmBorrow => {
|
||||
@ -541,6 +658,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
|
||||
fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
|
||||
debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id);
|
||||
|
||||
if !self.capture_information.contains_key(&assignee_place.place) {
|
||||
self.init_capture_info_for_place(assignee_place, diag_expr_id);
|
||||
}
|
||||
|
||||
self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id);
|
||||
}
|
||||
}
|
||||
@ -548,3 +670,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
||||
fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
|
||||
tcx.hir().name(var_hir_id)
|
||||
}
|
||||
|
||||
fn new_capture_analysis() -> bool {
|
||||
matches!(env::var("SG_NEW"), Ok(_))
|
||||
}
|
||||
|
||||
fn log_capture_analysis() -> bool {
|
||||
matches!(env::var("SG_VERBOSE"), Ok(_))
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ pub enum MutateMode {
|
||||
// This is the code that actually walks the tree.
|
||||
pub struct ExprUseVisitor<'a, 'tcx> {
|
||||
mc: mc::MemCategorizationContext<'a, 'tcx>,
|
||||
body_owner: LocalDefId,
|
||||
delegate: &'a mut dyn Delegate<'tcx>,
|
||||
}
|
||||
|
||||
@ -110,6 +111,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
) -> Self {
|
||||
ExprUseVisitor {
|
||||
mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results),
|
||||
body_owner,
|
||||
delegate,
|
||||
}
|
||||
}
|
||||
@ -529,7 +531,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat);
|
||||
|
||||
let tcx = self.tcx();
|
||||
let ExprUseVisitor { ref mc, ref mut delegate } = *self;
|
||||
let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
|
||||
return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
|
||||
if let PatKind::Binding(_, canonical_id, ..) = pat.kind {
|
||||
debug!("walk_pat: binding place={:?} pat={:?}", place, pat,);
|
||||
@ -569,31 +571,49 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
}));
|
||||
}
|
||||
|
||||
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) {
|
||||
// FIXME(arora-aman): fix the fn_decl_span
|
||||
fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, _fn_decl_span: Span) {
|
||||
debug!("walk_captures({:?})", closure_expr);
|
||||
|
||||
let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id);
|
||||
if let Some(upvars) = self.tcx().upvars_mentioned(closure_def_id) {
|
||||
for &var_id in upvars.keys() {
|
||||
let upvar_id = ty::UpvarId {
|
||||
var_path: ty::UpvarPath { hir_id: var_id },
|
||||
closure_expr_id: closure_def_id,
|
||||
// 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 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 (place, capture_info) in closure_capture_information.iter() {
|
||||
let var_hir_id = if let PlaceBase::Upvar(upvar_id) = place.base {
|
||||
upvar_id.var_path.hir_id
|
||||
} else {
|
||||
continue;
|
||||
// FIXME(arora-aman): throw err?
|
||||
};
|
||||
let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id);
|
||||
let captured_place = return_if_err!(self.cat_captured_var(
|
||||
closure_expr.hir_id,
|
||||
fn_decl_span,
|
||||
var_id,
|
||||
));
|
||||
match upvar_capture {
|
||||
|
||||
if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
|
||||
// The nested closure might be capturing our local variables
|
||||
// Since for the current body these aren't captures, we will ignore them.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The place is being captured by the enclosing closure
|
||||
// FIXME(arora-aman) Make sure this is valid to do when called from clippy.
|
||||
let upvar_id = ty::UpvarId::new(var_hir_id, self.body_owner);
|
||||
let place_with_id = PlaceWithHirId::new(
|
||||
capture_info.expr_id.unwrap_or(closure_expr.hir_id),
|
||||
place.base_ty,
|
||||
PlaceBase::Upvar(upvar_id),
|
||||
place.projections.clone(),
|
||||
);
|
||||
match capture_info.capture_kind {
|
||||
ty::UpvarCapture::ByValue(_) => {
|
||||
let mode = copy_or_move(&self.mc, &captured_place);
|
||||
self.delegate.consume(&captured_place, captured_place.hir_id, mode);
|
||||
let mode = copy_or_move(&self.mc, &place_with_id);
|
||||
self.delegate.consume(&place_with_id, place_with_id.hir_id, mode);
|
||||
}
|
||||
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
||||
self.delegate.borrow(
|
||||
&captured_place,
|
||||
captured_place.hir_id,
|
||||
&place_with_id,
|
||||
place_with_id.hir_id,
|
||||
upvar_borrow.kind,
|
||||
);
|
||||
}
|
||||
@ -601,18 +621,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_captured_var(
|
||||
&mut self,
|
||||
closure_hir_id: hir::HirId,
|
||||
closure_span: Span,
|
||||
var_id: hir::HirId,
|
||||
) -> mc::McResult<PlaceWithHirId<'tcx>> {
|
||||
// Create the place for the variable being borrowed, from the
|
||||
// perspective of the creator (parent) of the closure.
|
||||
let var_ty = self.mc.node_ty(var_id)?;
|
||||
self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id))
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_or_move<'a, 'tcx>(
|
||||
|
@ -8,8 +8,8 @@ LL | assert_send(|| {
|
||||
| ^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
|
||||
|
|
||||
= help: the trait `Sync` is not implemented for `Cell<i32>`
|
||||
= note: required because of the requirements on the impl of `Send` for `&'_#3r Cell<i32>`
|
||||
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell<i32>) _#17t]`
|
||||
= note: required because of the requirements on the impl of `Send` for `&'_#4r Cell<i32>`
|
||||
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#4r Cell<i32>) _#17t]`
|
||||
|
||||
error: generator cannot be shared between threads safely
|
||||
--> $DIR/generator-print-verbose-2.rs:12:5
|
||||
|
Loading…
Reference in New Issue
Block a user