]> git.lizzy.rs Git - rust.git/commitdiff
Use Places to express closure/generator Captures
authorAman Arora <me@aman-arora.com>
Wed, 9 Sep 2020 05:18:28 +0000 (01:18 -0400)
committerAman Arora <me@aman-arora.com>
Wed, 11 Nov 2020 01:44:47 +0000 (20:44 -0500)
Co-authored-by: Archer Zhang <archer.xn@gmail.com>
compiler/rustc_middle/src/ty/context.rs
compiler/rustc_middle/src/ty/mod.rs
compiler/rustc_mir_build/src/thir/cx/expr.rs
compiler/rustc_typeck/src/check/upvar.rs
compiler/rustc_typeck/src/expr_use_visitor.rs
src/test/ui/generator/print/generator-print-verbose-2.stderr

index 1c6937e685c65d3c6ceba23d3d10744cd8733261..e9bc4b9b90dd5621b8d8182a7958dfe008befd4d 100644 (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 @@ pub fn new(hir_owner: LocalDefId) -> 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 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
             tainted_by_errors,
             ref concrete_opaque_types,
             ref closure_captures,
+            ref closure_capture_information,
             ref generator_interior_types,
         } = *self;
 
@@ -709,6 +715,7 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas
             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);
         })
     }
index 0042b4a3a42793aa2fb545b12a182604b2aa27d6..5f2d3b7818e7f9c8d95e907d563121532645b7bb 100644 (file)
@@ -6,6 +6,7 @@
 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),
index 6ed7ed575fcb525649e7b2a4ceada9b28ba85226..47c0400533bd828f1e46e8b2140a29843d6c8c7b 100644 (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())
index e9dfef718fde9647d6bec54e4dc3e655075b0d76..24bb7756ef36fab34d2e70734d38a3218401e5da 100644 (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;
 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 @@ fn analyze_closure(
             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);
+                }
             }
         }
 
@@ -153,9 +137,11 @@ fn analyze_closure(
         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 @@ fn analyze_closure(
             }
         }
 
-        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 @@ fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec<Ty<'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 @@ fn final_upvar_tys(&self, closure_id: hir::HirId) -> Vec<Ty<'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 @@ fn adjust_upvar_borrow_kind_for_consume(
             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 @@ fn adjust_upvar_borrow_kind_for_mut(
             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 @@ fn adjust_upvar_borrow_kind_for_mut(
                     _ => (),
                 }
             }
-            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 @@ fn adjust_upvar_borrow_kind_for_unique(
             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 @@ fn adjust_upvar_deref(
         // 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 @@ fn adjust_upvar_deref(
     /// 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 @@ fn adjust_closure_kind(
             }
         }
     }
+
+    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 @@ fn consume(
             "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 @@ 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 => {
@@ -541,6 +658,11 @@ fn borrow(
 
     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 @@ fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::H
 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(_))
+}
index 57bd89b9d3da9393c0546d9511561a7e9e939f99..a8cac3e0fc820222ca2d273df8b79f4bc227c638 100644 (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 @@ pub fn new(
     ) -> Self {
         ExprUseVisitor {
             mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results),
+            body_owner,
             delegate,
         }
     }
@@ -529,7 +531,7 @@ fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) {
         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 @@ fn walk_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::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,
                         );
                     }
@@ -601,18 +621,6 @@ fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) {
             }
         }
     }
-
-    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>(
index f23949091d912fe0ef99760e3fdcdfe9531f13bb..d590f876b8e77c9acfcf6cee3711fd0087f3868b 100644 (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