Use Places to express closure/generator Captures

Co-authored-by: Archer Zhang <archer.xn@gmail.com>
This commit is contained in:
Aman Arora 2020-09-09 01:18:28 -04:00
parent cf9cf7c923
commit 127a6ede1d
6 changed files with 313 additions and 129 deletions

View file

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

View file

@ -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),

View file

@ -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())

View file

@ -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(_))
}

View file

@ -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>(

View file

@ -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