use super::FnCtxt;
+use std::env;
+
use crate::expr_use_visitor as euv;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir as hir;
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>) {
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 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 local_def_id = closure_def_id.expect_local();
- 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);
+ 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);
+
+ 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);
+ }
}
}
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,
}
}
- 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
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);
})
.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> {
// 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
// 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> {
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
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() {
_ => (),
}
}
- 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);
}
}
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 {
// 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.
/// 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)
}
}
}
+
+ 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> {
"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(
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 => {
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);
}
}
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(_))
+}
// 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>,
}
) -> Self {
ExprUseVisitor {
mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results),
+ body_owner,
delegate,
}
}
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,);
}));
}
- 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,
);
}
}
}
}
-
- 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>(