From 127a6ede1dd9622db20bee435221205b3a61d0ba Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Wed, 9 Sep 2020 01:18:28 -0400 Subject: [PATCH 01/16] Use Places to express closure/generator Captures Co-authored-by: Archer Zhang --- compiler/rustc_middle/src/ty/context.rs | 7 + compiler/rustc_middle/src/ty/mod.rs | 38 +++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 5 +- compiler/rustc_typeck/src/check/upvar.rs | 320 ++++++++++++------ compiler/rustc_typeck/src/expr_use_visitor.rs | 70 ++-- .../print/generator-print-verbose-2.stderr | 4 +- 6 files changed, 314 insertions(+), 130 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1c6937e685c65..e9bc4b9b90dd5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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>, @@ -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> 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> 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); }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0042b4a3a4279..5f2d3b7818e7f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -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, + + /// Capture mode that was selected + pub capture_kind: UpvarCapture<'tcx>, +} + pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; +/// 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, CaptureInfo<'tcx>>>; + #[derive(Clone, Copy, PartialEq, Eq)] pub enum IntVarValue { IntType(ast::IntTy), diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 6ed7ed575fcb5..47c0400533bd8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -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()) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index e9dfef718fde9..24bb7756ef36f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -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 = - 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::, 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 @@ 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 = 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, 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(_)) +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 57bd89b9d3da9..a8cac3e0fc820 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -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> { - // 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>( diff --git a/src/test/ui/generator/print/generator-print-verbose-2.stderr b/src/test/ui/generator/print/generator-print-verbose-2.stderr index f23949091d912..d590f876b8e77 100644 --- a/src/test/ui/generator/print/generator-print-verbose-2.stderr +++ b/src/test/ui/generator/print/generator-print-verbose-2.stderr @@ -8,8 +8,8 @@ LL | assert_send(|| { | ^^^^^^^^^^^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` - = note: required because of the requirements on the impl of `Send` for `&'_#3r Cell` - = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell) _#17t]` + = note: required because of the requirements on the impl of `Send` for `&'_#4r Cell` + = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#4r Cell) _#17t]` error: generator cannot be shared between threads safely --> $DIR/generator-print-verbose-2.rs:12:5 From 88310cc0ebf4144205743dbb3a65223deffcf8e6 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 11 Oct 2020 00:14:11 -0400 Subject: [PATCH 02/16] Indroduce feature flag for RFC-2229 Signed-off-by: Aman Arora --- compiler/rustc_feature/src/active.rs | 4 ++ compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 + compiler/rustc_typeck/src/check/upvar.rs | 41 +++++++++++-------- .../feature-gate-capture_disjoint_fields.rs | 12 ++++++ ...eature-gate-capture_disjoint_fields.stderr | 21 ++++++++++ ...eature-gate-capture_disjoint_fields.stdout | 1 + 7 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0df67b63eba58..a035507924794 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -616,6 +616,9 @@ declare_features! ( /// Enables `#[cfg(panic = "...")]` config key. (active, cfg_panic, "1.49.0", Some(77443), None), + /// Allows capturing disjoint fields in a closure/generator (RFC 2229). + (active, capture_disjoint_fields, "1.49.0", Some(53488), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -639,6 +642,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::inline_const, sym::repr128, sym::unsized_locals, + sym::capture_disjoint_fields, ]; /// Some features are not allowed to be used together at the same time, if diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5c5cf609ac33c..fa8edba629e92 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -547,6 +547,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), + rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ad58f89d87da7..3a2a3adce35c9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -318,6 +318,7 @@ symbols! { call_mut, call_once, caller_location, + capture_disjoint_fields, cdylib, ceilf32, ceilf64, @@ -909,6 +910,7 @@ symbols! { rustc_args_required_const, rustc_attrs, rustc_builtin_macro, + rustc_capture_analysis, rustc_clean, rustc_const_stable, rustc_const_unstable, diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 24bb7756ef36f..a28744c2ece75 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -32,8 +32,6 @@ use super::FnCtxt; -use std::env; - use crate::expr_use_visitor as euv; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; @@ -43,8 +41,25 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_span::sym; use rustc_span::{Span, Symbol}; +macro_rules! log_capture_analysis { + ($fcx:expr, $closure_def_id:expr, $fmt:literal) => { + if $fcx.should_log_capture_analysis($closure_def_id) { + print!("For closure={:?}: ", $closure_def_id); + println!($fmt); + } + }; + + ($fcx:expr, $closure_def_id:expr, $fmt:literal, $($args:expr),*) => { + if $fcx.should_log_capture_analysis($closure_def_id) { + print!("For closure={:?}: ", $closure_def_id); + println!($fmt, $($args),*); + } + }; +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { InferBorrowKindVisitor { fcx: self }.visit_body(body); @@ -115,8 +130,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); - if !new_capture_analysis() { - debug!("Using old-style capture analysis"); + if self.tcx.features().capture_disjoint_fields { + log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis"); + } else { + log_capture_analysis!(self, closure_def_id, "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); @@ -325,6 +342,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { projections: Default::default(), } } + + fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool { + self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) + } } struct InferBorrowKind<'a, 'tcx> { @@ -601,10 +622,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { 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); @@ -670,11 +687,3 @@ 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(_)) -} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs new file mode 100644 index 0000000000000..5eab718736cb2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -0,0 +1,12 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +fn main() { + let s = format!("s"); + + let c = #[rustc_capture_analysis] || { + //~^ ERROR: attributes on expressions are experimental + println!("This uses new capture analyysis to capture s={}", s); + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr new file mode 100644 index 0000000000000..4dc1f9a6ab27b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] || { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/feature-gate-capture_disjoint_fields.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout new file mode 100644 index 0000000000000..c1fca9afd3910 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout @@ -0,0 +1 @@ +For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis From 58e8f8fd2cac4cfcde6c7a1488dd8657dcacaad8 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 12 Oct 2020 20:16:02 -0400 Subject: [PATCH 03/16] Add initial set of testcases for RFC 2229 Co-authored-by: Dhruv Jauhar --- compiler/rustc_typeck/src/check/upvar.rs | 7 ++ .../arrays-completely-captured.rs | 17 ++++ .../arrays-completely-captured.stderr | 21 ++++ .../arrays-completely-captured.stdout | 20 ++++ .../capture-disjoint-field-struct.rs | 26 +++++ .../capture-disjoint-field-struct.stderr | 21 ++++ .../capture-disjoint-field-struct.stdout | 28 ++++++ .../capture-disjoint-field-tuple.rs | 21 ++++ .../capture-disjoint-field-tuple.stderr | 21 ++++ .../capture-disjoint-field-tuple.stdout | 28 ++++++ .../feature-gate-capture_disjoint_fields.rs | 3 +- ...eature-gate-capture_disjoint_fields.stderr | 2 +- ...eature-gate-capture_disjoint_fields.stdout | 19 ++++ .../filter-on-struct-member.rs | 40 ++++++++ .../filter-on-struct-member.stderr | 28 ++++++ .../filter-on-struct-member.stdout | 32 ++++++ .../multilevel-path-1.rs | 34 +++++++ .../multilevel-path-1.stderr | 21 ++++ .../multilevel-path-1.stdout | 28 ++++++ .../multilevel-path-2.rs | 30 ++++++ .../multilevel-path-2.stderr | 21 ++++ .../multilevel-path-2.stdout | 35 +++++++ .../2229_closure_analysis/nested-closure.rs | 40 ++++++++ .../nested-closure.stderr | 30 ++++++ .../nested-closure.stdout | 98 +++++++++++++++++++ .../path-with-array-access.rs | 28 ++++++ .../path-with-array-access.stderr | 21 ++++ .../path-with-array-access.stdout | 28 ++++++ .../simple-struct-min-capture.rs | 33 +++++++ .../simple-struct-min-capture.stderr | 21 ++++ .../simple-struct-min-capture.stdout | 45 +++++++++ .../2229_closure_analysis/slice-pat.rs | 28 ++++++ .../2229_closure_analysis/slice-pat.stderr | 21 ++++ .../2229_closure_analysis/slice-pat.stdout | 20 ++++ 34 files changed, 914 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stdout diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index a28744c2ece75..9e4e656536152 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -169,6 +169,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); + log_capture_analysis!( + self, + closure_def_id, + "capture information: {:#?}", + delegate.capture_information + ); + if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs new file mode 100644 index 0000000000000..a5dd202bc072b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -0,0 +1,17 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Ensure that capture analysis results in arrays being completely captured. +fn main() { + let mut m = [1, 2, 3, 4, 5]; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + m[0] += 10; + m[1] += 40; + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr new file mode 100644 index 0000000000000..3d3912d0796ee --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/arrays-completely-captured.rs:9:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/arrays-completely-captured.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout new file mode 100644 index 0000000000000..b4142a4fd8e46 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout @@ -0,0 +1,20 @@ +For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): capture information: { + Place { + base_ty: [i32; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), + local_id: 12, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#6r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs new file mode 100644 index 0000000000000..a2be21cddb48c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -0,0 +1,26 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", p.x); + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr new file mode 100644 index 0000000000000..9233597c360d2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-struct.rs:15:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-struct.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout new file mode 100644 index 0000000000000..ab7bd60e48d1c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), + local_id: 31, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs new file mode 100644 index 0000000000000..e06cde73158ea --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -0,0 +1,21 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", t.0); + }; + + // `c` only captures t.0, therefore mutating t.1 is allowed. + let t1 = &mut t.1; + + c(); + *t1 = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr new file mode 100644 index 0000000000000..f83487ecce555 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-tuple.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-tuple.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout new file mode 100644 index 0000000000000..517d7564c72ce --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): capture information: { + Place { + base_ty: (i32, i32), + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), + local_id: 28, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index 5eab718736cb2..072ed8eeab6f2 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -5,8 +5,9 @@ fn main() { let s = format!("s"); - let c = #[rustc_capture_analysis] || { + let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + || { println!("This uses new capture analyysis to capture s={}", s); }; } diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr index 4dc1f9a6ab27b..133de1d13e85d 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -1,7 +1,7 @@ error[E0658]: attributes on expressions are experimental --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 | -LL | let c = #[rustc_capture_analysis] || { +LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout index c1fca9afd3910..40ac31b4ad90b 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout @@ -1 +1,20 @@ For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): capture information: { + Place { + base_ty: std::string::String, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), + local_id: 52, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#50r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs new file mode 100644 index 0000000000000..aa251c4526cf3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -0,0 +1,40 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Filter { + div: i32, +} +impl Filter { + fn allowed(&self, x: i32) -> bool { + x % self.div == 1 + } +} + +struct Data { + filter: Filter, + list: Vec, +} +impl Data { + fn update(&mut self) { + // The closure passed to filter only captures self.filter, + // therefore mutating self.list is allowed. + self.list.retain( + //~^ cannot borrow `self.list` as mutable because it is also borrowed as immutable + #[rustc_capture_analysis] + |v| self.filter.allowed(*v), + ); + } +} + +fn main() { + let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() }; + + for i in 1..10 { + d.list.push(i); + } + + d.update(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr new file mode 100644 index 0000000000000..3eb4decdeae29 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -0,0 +1,28 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/filter-on-struct-member.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0502]: cannot borrow `self.list` as mutable because it is also borrowed as immutable + --> $DIR/filter-on-struct-member.rs:22:9 + | +LL | self.list.retain( + | ^ ------ immutable borrow later used by call + | _________| + | | +LL | | +LL | | #[rustc_capture_analysis] +LL | | |v| self.filter.allowed(*v), + | | --- ---- first borrow occurs due to use of `self` in closure + | | | + | | immutable borrow occurs here +LL | | ); + | |_________^ mutable borrow occurs here + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout new file mode 100644 index 0000000000000..560b2aa3b57b2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout @@ -0,0 +1,32 @@ +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): Using new-style capture analysis +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): capture information: { + Place { + base_ty: &mut Data, + base: Upvar( + UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), + ), + projections: [ + Projection { + ty: Data, + kind: Deref, + }, + Projection { + ty: Filter, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), + local_id: 13, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#7r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs new file mode 100644 index 0000000000000..bd8d52d6a3c7e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -0,0 +1,34 @@ +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + // Only paths that appears within the closure that directly start off + // a variable defined outside the closure are captured. + // + // Therefore `w.p` is captured + // Note that `wp.x` doesn't start off a variable defined outside the closure. + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let wp = &w.p; + println!("{}", wp.x); + }; + + // Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr new file mode 100644 index 0000000000000..bd339a68fa0c6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-1.rs:22:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-1.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout new file mode 100644 index 0000000000000..525366cb964f0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_1[317d]::main), + local_id: 20, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#37r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs new file mode 100644 index 0000000000000..a8aca53bc73fd --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -0,0 +1,30 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", w.p.x); + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr new file mode 100644 index 0000000000000..772dfd643eaa0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-2.rs:19:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-2.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout new file mode 100644 index 0000000000000..f89670c8b7f32 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout @@ -0,0 +1,35 @@ +For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_2[317d]::main), + local_id: 35, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs new file mode 100644 index 0000000000000..64b69af0f0c1c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -0,0 +1,40 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +// This testcase ensures that nested closures are handles properly +// - The nested closure is analyzed first. +// - The capture kind of the nested closure is accounted for by the enclosing closure +// - Any captured path by the nested closure that starts off a local variable in the enclosing +// closure is not listed as a capture of the enclosing closure. + +fn main() { + let mut p = Point { x: 5, y: 20 }; + + let mut c1 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", p.x); + let incr = 10; + let mut c2 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || p.y += incr; + c2(); + println!("{}", p.y); + }; + + c1(); + + let px = &p.x; + + println!("{}", px); + + c1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr new file mode 100644 index 0000000000000..dbd9e3655a323 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -0,0 +1,30 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:19:18 + | +LL | let mut c1 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:24:22 + | +LL | let mut c2 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/nested-closure.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout new file mode 100644 index 0000000000000..84d87a75bda98 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout @@ -0,0 +1,98 @@ +For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): Using new-style capture analysis +For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#109r), + ), + }, + Place { + base_ty: i32, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 72, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#110r), + ), + }, +} +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 37, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#114r), + ), + }, + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#115r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs new file mode 100644 index 0000000000000..c967c0b72d409 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -0,0 +1,28 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Point { + x: f32, + y: f32, +} + +struct Pentagon { + points: [Point; 5], +} + +fn main() { + let p1 = Point { x: 10.0, y: 10.0 }; + let p2 = Point { x: 7.5, y: 12.5 }; + let p3 = Point { x: 15.0, y: 15.0 }; + let p4 = Point { x: 12.5, y: 12.5 }; + let p5 = Point { x: 20.0, y: 10.0 }; + + let pent = Pentagon { points: [p1, p2, p3, p4, p5] }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", pent.points[5].x); + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr new file mode 100644 index 0000000000000..84bcc55d99c97 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/path-with-array-access.rs:23:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-with-array-access.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout new file mode 100644 index 0000000000000..b843b0494b93c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Pentagon, + base: Upvar( + UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: [Point; 5], + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:9 ~ path_with_array_access[317d]::main), + local_id: 83, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#34r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs new file mode 100644 index 0000000000000..27ab9d6b7359e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -0,0 +1,33 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure that min analysis meets capture kind for all paths captured. + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 20 }; + + // + // Requirements: + // p.x -> MutBoorrow + // p -> ImmBorrow + // + // Requirements met when p is captured via MutBorrow + // + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + p.x += 10; + println!("{:?}", p); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr new file mode 100644 index 0000000000000..002d2aaab89a5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/simple-struct-min-capture.rs:25:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple-struct-min-capture.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout new file mode 100644 index 0000000000000..02129f1acb55c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout @@ -0,0 +1,45 @@ +For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 15, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#34r), + ), + }, + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 35, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs new file mode 100644 index 0000000000000..fc966c4193e99 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs @@ -0,0 +1,28 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure Index projections are handled properly during capture analysis +// +// The array should be moved in entirety, even though only some elements are used. + +fn main() { + let arr : [String; 5] = [ + format!("A"), + format!("B"), + format!("C"), + format!("D"), + format!("E") + ]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let [a, b, .., e] = arr; + assert_eq!(a, "A"); + assert_eq!(b, "B"); + assert_eq!(e, "E"); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr new file mode 100644 index 0000000000000..5c6b505d64fd3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/slice-pat.rs:18:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/slice-pat.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout new file mode 100644 index 0000000000000..ba9b2bca19679 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout @@ -0,0 +1,20 @@ +For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information: { + Place { + base_ty: [std::string::String; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: std::string::String, + kind: Index, + }, + ], + }: CaptureInfo { + expr_id: None, + capture_kind: ByValue( + None, + ), + }, +} From 145312075f02bf4303f5b638f4c6187f43900f1c Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sat, 17 Oct 2020 01:49:11 -0400 Subject: [PATCH 04/16] Add helper function for Capture Esclations and expressions Co-authored-by: Dhruv Jauhar --- compiler/rustc_middle/src/ty/mod.rs | 18 ++- compiler/rustc_typeck/src/check/upvar.rs | 148 ++++++++++-------- .../2229_closure_analysis/slice-pat.stdout | 11 +- 3 files changed, 110 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5f2d3b7818e7f..52b49184cf186 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -765,7 +765,23 @@ pub struct UpvarBorrow<'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 + /// Expr Id pointing to use that resulted in selecting the current capture kind + /// + /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is + /// possible that we don't see the use of a particular place resulting in expr_id being + /// None. In such case we fallback on uvpars_mentioned for span. + /// + /// Eg: + /// ```rust + /// let x = ...; + /// + /// let c = || { + /// let _ = x + /// } + /// ``` + /// + /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, + /// but we won't see it being used during capture analysis, since it's essentially a discard. pub expr_id: Option, /// Capture mode that was selected diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9e4e656536152..6365797148547 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -284,30 +284,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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) = + let new_capture_kind = if let Some(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, - } - } - }; - } + // upvar_capture_map only stores the UpvarCapture (CaptureKind), + // so we create a fake capture info with no expression. + let fake_capture_info = + ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; + self.determine_capture_info(fake_capture_info, capture_info.clone()).capture_kind + } else { + capture_info.capture_kind + }; self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind); } @@ -353,6 +340,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool { self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } + + /// Helper function to determine if we need to escalate CaptureKind from + /// CaptureInfo A to B and returns the escalated CaptureInfo. + /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) + /// + /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based + /// on the `CaptureInfo` containing an associated expression id. + /// + /// If both the CaptureKind and Expression are considered to be equivalent, + /// then `CaptureInfo` A is preferred. + fn determine_capture_info( + &self, + capture_info_a: ty::CaptureInfo<'tcx>, + capture_info_b: ty::CaptureInfo<'tcx>, + ) -> ty::CaptureInfo<'tcx> { + // If the capture kind is equivalent then, we don't need to escalate and can compare the + // expressions. + let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => true, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + ref_a.kind == ref_b.kind + } + _ => false, + }; + + if eq_capture_kind { + match (capture_info_a.expr_id, capture_info_b.expr_id) { + (Some(_), _) | (None, None) => capture_info_a, + (None, Some(_)) => capture_info_b, + } + } else { + match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), _) => capture_info_a, + (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + match (ref_a.kind, ref_b.kind) { + // Take LHS: + (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) + | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, + + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, + + (ty::ImmBorrow, ty::ImmBorrow) + | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) + | (ty::MutBorrow, ty::MutBorrow) => { + bug!("Expected unequal capture kinds"); + } + } + } + } + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -426,16 +467,10 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { 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, - }; + let curr_info = self.capture_information[&place_with_id.place]; + let updated_info = self.fcx.determine_capture_info(curr_info, capture_info); - self.capture_information.insert(place_with_id.place.clone(), updated_info); + self.capture_information[&place_with_id.place] = updated_info; } /// Indicates that `place_with_id` is being directly mutated (e.g., assigned @@ -532,42 +567,28 @@ impl<'a, 'tcx> InferBorrowKind<'a, '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 + let curr_capture_info = self.capture_information[&place_with_id.place]; debug!( - "adjust_upvar_borrow_kind(place={:?}, , diag_expr_id={:?}, capture_info={:?}, kind={:?})", - place_with_id, diag_expr_id, capture_info, kind + "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})", + place_with_id, diag_expr_id, curr_capture_info, kind ); - match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => { - // Upvar is already by-value, the strongest criteria. - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - match (upvar_borrow.kind, kind) { - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => { - 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) - | (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow) - | (ty::MutBorrow, _) => {} - } - } - } + if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind { + // It's already captured by value, we don't need to do anything here + return; + } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind { + // Use the same region as the current capture information + // Doesn't matter since only one of the UpvarBorrow will be used. + let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; + + let capture_info = ty::CaptureInfo { + expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), + }; + let updated_info = self.fcx.determine_capture_info(curr_capture_info, capture_info); + self.capture_information[&place_with_id.place] = updated_info; + }; } fn adjust_closure_kind( @@ -622,7 +643,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { 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); diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout index ba9b2bca19679..3e352e2c52584 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout @@ -12,9 +12,16 @@ For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information }, ], }: CaptureInfo { - expr_id: None, + expr_id: Some( + HirId { + owner: DefId(0:3 ~ slice_pat[317d]::main), + local_id: 179, + }, + ), capture_kind: ByValue( - None, + Some( + $DIR/slice-pat.rs:21:33: 21:36 (#0), + ), ), }, } From 8f0c0d656d5ca2b17910ec7990691ae5dcd7c1e4 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sat, 26 Sep 2020 17:07:00 -0400 Subject: [PATCH 05/16] Initial work for doing minimum capture analysis for RFC-2229 Co-authored-by: Chris Pardy Co-authored-by: Logan Mosier --- compiler/rustc_middle/src/ty/context.rs | 13 +- compiler/rustc_middle/src/ty/mod.rs | 30 +- .../src/borrow_check/diagnostics/mod.rs | 27 +- compiler/rustc_typeck/src/check/upvar.rs | 318 ++++++++++++++++-- compiler/rustc_typeck/src/expr_use_visitor.rs | 101 +++--- 5 files changed, 374 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e9bc4b9b90dd5..76ca0c51ce1c3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,9 +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>, + /// Given the closure DefId this map provides a map of + /// root variables to minimum set of `Place`s (and how) that need to be tracked + /// to support all captures of that closure. + pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>, /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). @@ -446,7 +447,7 @@ impl<'tcx> TypeckResults<'tcx> { tainted_by_errors: None, concrete_opaque_types: Default::default(), closure_captures: Default::default(), - closure_capture_information: Default::default(), + closure_min_captures: Default::default(), generator_interior_types: Default::default(), } } @@ -681,7 +682,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors, ref concrete_opaque_types, ref closure_captures, - ref closure_capture_information, + ref closure_min_captures, ref generator_interior_types, } = *self; @@ -715,7 +716,7 @@ impl<'a, 'tcx> HashStable> 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); + closure_min_captures.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 52b49184cf186..c41463d18448d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -788,30 +788,18 @@ pub struct CaptureInfo<'tcx> { pub capture_kind: UpvarCapture<'tcx>, } +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct CapturedPlace<'tcx> { + pub place: HirPlace<'tcx>, + pub info: CaptureInfo<'tcx>, +} + pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; -/// 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, CaptureInfo<'tcx>>>; +pub type MinCaptureList<'tcx> = Vec>; +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; +pub type MinCaptureInformationMap<'tcx> = FxHashMap>; #[derive(Clone, Copy, PartialEq, Eq)] pub enum IntVarValue { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 4256f6e39d5e8..41f3edaa41380 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -383,16 +383,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field_from_ty(&ty, field, variant_index) } ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { - // `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case - // the closure comes from another crate. But in that case we wouldn't - // be borrowck'ing it, so we can just unwrap: - let (&var_id, _) = self - .infcx - .tcx - .upvars_mentioned(def_id) - .unwrap() - .get_index(field.index()) - .unwrap(); + // We won't be borrowck'ing here if the closure came from another crate, + // so it's safe to call `expect_local`. + // + // We know the field exists so it's safe to call operator[] and `unwrap` here. + let (&var_id, _) = + self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] + .get_index(field.index()) + .unwrap(); self.infcx.tcx.hir().name(var_id).to_string() } @@ -967,9 +965,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { - for ((upvar_hir_id, upvar), place) in - self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places) + for (upvar_hir_id, place) in + self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] + .keys() + .zip(places) { + let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span; match place { Operand::Copy(place) | Operand::Move(place) if target_place == place.as_ref() => @@ -991,7 +992,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let usage_span = match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) { ty::UpvarCapture::ByValue(Some(span)) => span, - _ => upvar.span, + _ => span, }; return Some((*args_span, generator_kind, usage_span)); } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 6365797148547..4b6d10357ef11 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -39,11 +39,13 @@ 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::{Place, PlaceBase, PlaceWithHirId}; +use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::sym; use rustc_span::{Span, Symbol}; +use std::env; + macro_rules! log_capture_analysis { ($fcx:expr, $closure_def_id:expr, $fmt:literal) => { if $fcx.should_log_capture_analysis($closure_def_id) { @@ -60,6 +62,17 @@ macro_rules! log_capture_analysis { }; } +/// Describe the relationship between the paths of two places +/// eg: +/// - foo is ancestor of foo.bar.baz +/// - foo.bar.baz is an descendant of foo.bar, +/// - foo.bar and foo.baz are divergent +enum PlaceAncestryRelation { + Ancestor, + Descendant, + Divergent, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { InferBorrowKindVisitor { fcx: self }.visit_body(body); @@ -130,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); - if self.tcx.features().capture_disjoint_fields { + if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) { log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis"); } else { log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis"); @@ -192,12 +205,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.set_closure_captures(closure_def_id, &delegate); - - self.typeck_results - .borrow_mut() - .closure_capture_information - .insert(closure_def_id, delegate.capture_information); + self.compute_min_captures(closure_def_id, delegate); + self.set_closure_captures(closure_def_id); // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure @@ -266,43 +275,221 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - fn set_closure_captures( + /// Bridge for closure analysis + /// ---------------------------- + /// + /// For closure with DefId `c`, the bridge converts structures required for supporting RFC 2229, + /// to structures currently used in the compiler for handling closure captures. + /// + /// For example the following structure will be converted: + /// + /// closure_min_captures + /// foo -> [ {foo.x, ImmBorrow}, {foo.y, MutBorrow} ] + /// bar -> [ {bar.z, ByValue}, {bar.q, MutBorrow} ] + /// + /// to + /// + /// 1. closure_captures + /// foo -> UpvarId(foo, c), bar -> UpvarId(bar, c) + /// + /// 2. upvar_capture_map + /// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue + + fn set_closure_captures(&self, closure_def_id: DefId) { + let mut closure_captures: FxIndexMap = Default::default(); + let mut upvar_capture_map = ty::UpvarCaptureMap::default(); + + if let Some(min_captures) = + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) + { + for (var_hir_id, min_list) in min_captures.iter() { + for captured_place in min_list { + let place = &captured_place.place; + let capture_info = captured_place.info; + + let upvar_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id, + base => bug!("Expected upvar, found={:?}", base), + }; + + assert_eq!(upvar_id.var_path.hir_id, *var_hir_id); + assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local()); + + closure_captures.insert(*var_hir_id, upvar_id); + + let new_capture_kind = if let Some(capture_kind) = + upvar_capture_map.get(&upvar_id) + { + // upvar_capture_map only stores the UpvarCapture (CaptureKind), + // so we create a fake capture info with no expression. + let fake_capture_info = + ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; + self.determine_capture_info(fake_capture_info, capture_info.clone()) + .capture_kind + } else { + capture_info.capture_kind + }; + upvar_capture_map.insert(upvar_id, new_capture_kind); + } + } + } + debug!( + "For closure_def_id={:?}, set_closure_captures={:#?}", + closure_def_id, closure_captures + ); + debug!( + "For closure_def_id={:?}, upvar_capture_map={:#?}", + closure_def_id, upvar_capture_map + ); + + if !closure_captures.is_empty() { + self.typeck_results + .borrow_mut() + .closure_captures + .insert(closure_def_id, closure_captures); + + self.typeck_results.borrow_mut().upvar_capture_map.extend(upvar_capture_map); + } + } + + /// Analyses the information collected by InferBorrowKind to compute the min number of + /// Places (and corresponding capture kind) that we need to keep track of to support all + /// the required captured paths. + /// + /// Eg: + /// ```rust + /// struct Point { x: i32, y: i32 } + /// + /// let s: String; // hir_id_s + /// let mut p: Point; // his_id_p + /// let c = || { + /// println!("{}", s); // L1 + /// p.x += 10; // L2 + /// println!("{}" , p.y) // L3 + /// println!("{}", p) // L4 + /// drop(s); // L5 + /// }; + /// ``` + /// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on + /// the lines L1..5 respectively. + /// + /// InferBorrowKind results in a structure like this: + /// + /// ``` + /// { + /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue), + /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow)) + /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow)) + /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow)) + /// ``` + /// + /// After the min capture analysis, we get: + /// ``` + /// { + /// hir_id_s -> [ + /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue) + /// ], + /// hir_id_p -> [ + /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)), + /// ], + /// ``` + fn compute_min_captures( &self, closure_def_id: DefId, - inferred_info: &InferBorrowKind<'_, 'tcx>, + inferred_info: InferBorrowKind<'_, 'tcx>, ) { - let mut closure_captures: FxIndexMap = Default::default(); + let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); - for (place, capture_info) in inferred_info.capture_information.iter() { - let upvar_id = match place.base { - PlaceBase::Upvar(upvar_id) => upvar_id, + for (place, capture_info) in inferred_info.capture_information.into_iter() { + let var_hir_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), }; - 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 new_capture_kind = if let Some(capture_kind) = - self.typeck_results.borrow_mut().upvar_capture_map.get(&upvar_id) - { - // upvar_capture_map only stores the UpvarCapture (CaptureKind), - // so we create a fake capture info with no expression. - let fake_capture_info = - ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; - self.determine_capture_info(fake_capture_info, capture_info.clone()).capture_kind - } else { - capture_info.capture_kind + // Arrays are captured in entirety, drop Index projections and projections + // after Index projections. + let first_index_projection = + place.projections.split(|proj| ProjectionKind::Index == proj.kind).next(); + let place = Place { + base_ty: place.base_ty, + base: place.base, + projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()), + }; + + let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { + None => { + let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }]; + root_var_min_capture_list.insert(var_hir_id, min_cap_list); + continue; + } + Some(min_cap_list) => min_cap_list, }; - self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind); + + // Go through each entry in the current list of min_captures + // - if ancestor is found, update it's capture kind to account for current place's + // capture information. + // + // - if descendant is found, remove it from the list, and update the current place's + // capture information to account for the descendants's capture kind. + // + // We can never be in a case where the list contains both an ancestor and a descendant + // Also there can only be ancestor but in case of descendants there might be + // multiple. + + let mut descendant_found = false; + let mut updated_capture_info = capture_info; + min_cap_list.retain(|possible_descendant| { + match determine_place_ancestry_relation(&place, &possible_descendant.place) { + // current place is ancestor of possible_descendant + PlaceAncestryRelation::Ancestor => { + descendant_found = true; + updated_capture_info = self + .determine_capture_info(updated_capture_info, possible_descendant.info); + false + } + + _ => true, + } + }); + + let mut ancestor_found = false; + if !descendant_found { + for possible_ancestor in min_cap_list.iter_mut() { + match determine_place_ancestry_relation(&place, &possible_ancestor.place) { + // current place is descendant of possible_ancestor + PlaceAncestryRelation::Descendant => { + ancestor_found = true; + possible_ancestor.info = + self.determine_capture_info(possible_ancestor.info, capture_info); + + // Only one ancestor of the current place will be in the list. + break; + } + _ => {} + } + } + } + + // Only need to insert when we don't have an ancestor in the existing min capture list + if !ancestor_found { + let captured_place = + ty::CapturedPlace { place: place.clone(), info: updated_capture_info }; + min_cap_list.push(captured_place); + } } - if !closure_captures.is_empty() { + log_capture_analysis!( + self, + closure_def_id, + "min_captures={:#?}", + root_var_min_capture_list + ); + + if !root_var_min_capture_list.is_empty() { self.typeck_results .borrow_mut() - .closure_captures - .insert(closure_def_id, closure_captures); + .closure_min_captures + .insert(closure_def_id, root_var_min_capture_list); } } @@ -418,8 +605,27 @@ struct InferBorrowKind<'a, 'tcx> { // variable access that caused us to do so. current_origin: Option<(Span, Symbol)>, - // For each upvar that we access, we track the minimal kind of - // access we need (ref, ref mut, move, etc). + /// For each Place that we access, we track the minimal kind of + /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. + /// Consider closure where s.str1 is captured via an ImmutableBorrow and + /// s.str2 via a MutableBorrow + /// + /// ```rust + /// // Assume that the HirId for the variable definition is `V1` + /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } + /// + /// let fix_s = |new_s2| { + /// // Assume that the HirId for the expression `s.str1` is `E1` + /// println!("Updating SomeStruct with str1=", s.str1); + /// // Assume that the HirId for the expression `*s.str2` is `E2` + /// s.str2 = new_s2; + /// } + /// ``` + /// + /// For closure `fix_s`, (at a high level) the map contains + /// + /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } + /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } capture_information: FxIndexMap, ty::CaptureInfo<'tcx>>, } @@ -714,3 +920,45 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } + +/// Determines the Ancestry relationship of Place A relative to Place B +/// +/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B +/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B +/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other. +fn determine_place_ancestry_relation( + place_a: &Place<'tcx>, + place_b: &Place<'tcx>, +) -> PlaceAncestryRelation { + // If Place A and Place B, don't start off from the same root variable, they are divergent. + if place_a.base != place_b.base { + return PlaceAncestryRelation::Divergent; + } + + // Assume of length of projections_a = n + let projections_a = &place_a.projections; + + // Assume of length of projections_b = m + let projections_b = &place_b.projections; + + let mut same_initial_projections = true; + + for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) { + if proj_a != proj_b { + same_initial_projections = false; + break; + } + } + + if same_initial_projections { + // First min(n, m) projections are the same + // Select Ancestor/Descendant + if projections_b.len() >= projections_a.len() { + PlaceAncestryRelation::Ancestor + } else { + PlaceAncestryRelation::Descendant + } + } else { + PlaceAncestryRelation::Divergent + } +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index a8cac3e0fc820..83cc1da69851f 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -18,7 +18,6 @@ use rustc_middle::ty::{self, adjustment, TyCtxt}; use rustc_target::abi::VariantIdx; use crate::mem_categorization as mc; -use rustc_span::Span; /////////////////////////////////////////////////////////////////////////// // The Delegate trait @@ -331,8 +330,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_expr(base); } - hir::ExprKind::Closure(_, _, _, fn_decl_span, _) => { - self.walk_captures(expr, fn_decl_span); + hir::ExprKind::Closure(..) => { + self.walk_captures(expr); } hir::ExprKind::Box(ref base) => { @@ -571,51 +570,73 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } - // FIXME(arora-aman): fix the fn_decl_span - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, _fn_decl_span: Span) { + /// Handle the case where the current body contains a closure. + /// + /// When the current body being handled is a closure, then we must make sure that + /// - The parent closure only captures Places from the nested closure that are not local to it. + /// + /// In the following example the closures `c` only captures `p.x`` even though `incr` + /// is a capture of the nested closure + /// + /// ```rust + /// let p = ..; + /// let c = || { + /// let incr = 10; + /// let nested = || p.x += incr; + /// } + /// ``` + /// + /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing + /// closure as the DefId. + fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { debug!("walk_captures({:?})", closure_expr); - // 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? - }; - 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. + // For purposes of this function, generator and closures are equivalent. + let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() { + ty::Closure(..) | ty::Generator(..) => true, + _ => false, + }; + + if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) + { + for (var_hir_id, min_list) in min_captures.iter() { + if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) { + // The nested closure might be capturing the current (enclosing) closure's local variables. + // We check if the root variable is ever mentioned within the enclosing closure, if not + // then for the current body (if it's a closure) these aren't captures, we will ignore them. continue; } + for captured_place in min_list { + let place = &captured_place.place; + let capture_info = captured_place.info; - // The place is being captured by the enclosing closure - // 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, &place_with_id); - self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow( - &place_with_id, - place_with_id.hir_id, - upvar_borrow.kind, - ); + let upvar_id = if body_owner_is_closure { + // Mark the place to be captured by the enclosing closure + ty::UpvarId::new(*var_hir_id, self.body_owner) + } else { + ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local()) + }; + let place_with_id = PlaceWithHirId::new( + 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, &place_with_id); + self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); + } + ty::UpvarCapture::ByRef(upvar_borrow) => { + self.delegate.borrow( + &place_with_id, + place_with_id.hir_id, + upvar_borrow.kind, + ); + } } } } From b16815b58d2f584b0a337a3456c1238a96b3f443 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 13 Oct 2020 14:02:38 -0400 Subject: [PATCH 06/16] Update tests with min capture information Co-authored-by: Chris Pardy Co-authored-by: Logan Mosier --- .../arrays-completely-captured.stdout | 27 ++++ .../capture-disjoint-field-struct.stdout | 35 +++++ .../capture-disjoint-field-tuple.stdout | 35 +++++ ...eature-gate-capture_disjoint_fields.stdout | 27 ++++ .../filter-on-struct-member.stdout | 39 ++++++ .../multilevel-path-1.stdout | 35 +++++ .../multilevel-path-2.stdout | 42 ++++++ .../nested-closure.stdout | 123 ++++++++++++++++++ .../path-with-array-access.stdout | 35 +++++ .../simple-struct-min-capture.stdout | 27 ++++ .../2229_closure_analysis/slice-pat.stdout | 29 +++++ 11 files changed, 454 insertions(+) diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout index b4142a4fd8e46..e0cd06f765e07 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout @@ -18,3 +18,30 @@ For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): ca ), }, } +For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: [i32; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), + local_id: 12, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#6r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout index ab7bd60e48d1c..d378b12a23500 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): ), }, } +For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), + local_id: 31, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout index 517d7564c72ce..9a080c28d4ebf 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): ), }, } +For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: (i32, i32), + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), + local_id: 28, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout index 40ac31b4ad90b..69722f9d21ec2 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout @@ -18,3 +18,30 @@ For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closu ), }, } +For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: std::string::String, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), + local_id: 52, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#50r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout index 560b2aa3b57b2..44fb88e1520c4 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout @@ -30,3 +30,42 @@ For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closu ), }, } +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): min_captures={ + HirId { + owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: &mut Data, + base: Upvar( + UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), + ), + projections: [ + Projection { + ty: Data, + kind: Deref, + }, + Projection { + ty: Filter, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), + local_id: 13, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#7r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout index 525366cb964f0..63a3669d0a76d 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): capture inf ), }, } +For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:8 ~ multilevel_path_1[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_1[317d]::main), + local_id: 20, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#37r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout index f89670c8b7f32..576d2c36b6f25 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout @@ -33,3 +33,45 @@ For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): capture inf ), }, } +For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:8 ~ multilevel_path_2[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_2[317d]::main), + local_id: 35, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout index 84d87a75bda98..446419c75f5eb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout @@ -43,6 +43,66 @@ For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): c ), }, } +For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): min_captures={ + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#109r), + ), + }, + }, + ], + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 5, + }: [ + CapturedPlace { + place: Place { + base_ty: i32, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 72, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#110r), + ), + }, + }, + ], +} For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture information: { Place { @@ -96,3 +156,66 @@ For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture inform ), }, } +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 37, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#114r), + ), + }, + }, + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#115r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout index b843b0494b93c..73f880c155afd 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): captu ), }, } +For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:9 ~ path_with_array_access[317d]::main), + local_id: 6, + }: [ + CapturedPlace { + place: Place { + base_ty: Pentagon, + base: Upvar( + UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: [Point; 5], + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:9 ~ path_with_array_access[317d]::main), + local_id: 83, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#34r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout index 02129f1acb55c..c0421852d93d8 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout @@ -43,3 +43,30 @@ For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): cap ), }, } +For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 15, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#34r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout index 3e352e2c52584..43c43015134ae 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout @@ -25,3 +25,32 @@ For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information ), }, } +For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ slice_pat[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: [std::string::String; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ slice_pat[317d]::main), + local_id: 179, + }, + ), + capture_kind: ByValue( + Some( + $DIR/slice-pat.rs:21:33: 21:36 (#0), + ), + ), + }, + }, + ], +} From 825e9e45d14694d23fde29fdab2b02d9973a4eb3 Mon Sep 17 00:00:00 2001 From: Roxane Fruytier Date: Tue, 27 Oct 2020 23:41:52 -0400 Subject: [PATCH 07/16] Reduce verbosity of capture analysis logs Co-authored-by: Jenny Wills Co-authored-by: Aman Arora --- compiler/rustc_typeck/src/check/upvar.rs | 112 ++++++--- .../arrays-completely-captured.rs | 2 + .../arrays-completely-captured.stderr | 14 +- .../arrays-completely-captured.stdout | 47 ---- .../capture-disjoint-field-struct.rs | 2 + .../capture-disjoint-field-struct.stderr | 14 +- .../capture-disjoint-field-struct.stdout | 63 ----- .../capture-disjoint-field-tuple.rs | 2 + .../capture-disjoint-field-tuple.stderr | 18 +- .../capture-disjoint-field-tuple.stdout | 63 ----- .../feature-gate-capture_disjoint_fields.rs | 2 + ...eature-gate-capture_disjoint_fields.stderr | 14 +- ...eature-gate-capture_disjoint_fields.stdout | 47 ---- .../filter-on-struct-member.rs | 3 +- .../filter-on-struct-member.stderr | 29 +-- .../filter-on-struct-member.stdout | 71 ------ .../multilevel-path-1.rs | 2 + .../multilevel-path-1.stderr | 14 +- .../multilevel-path-1.stdout | 63 ----- .../multilevel-path-2.rs | 2 + .../multilevel-path-2.stderr | 14 +- .../multilevel-path-2.stdout | 77 ------ .../2229_closure_analysis/nested-closure.rs | 8 + .../nested-closure.stderr | 56 ++++- .../nested-closure.stdout | 221 ------------------ .../path-with-array-access.rs | 2 + .../path-with-array-access.stderr | 14 +- .../path-with-array-access.stdout | 63 ----- .../simple-struct-min-capture.rs | 3 + .../simple-struct-min-capture.stderr | 20 +- .../simple-struct-min-capture.stdout | 72 ------ .../2229_closure_analysis/slice-pat.rs | 2 + .../2229_closure_analysis/slice-pat.stderr | 14 +- .../2229_closure_analysis/slice-pat.stdout | 56 ----- 34 files changed, 297 insertions(+), 909 deletions(-) delete mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stdout diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 4b6d10357ef11..a01aa8531ca9c 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -46,22 +46,6 @@ use rustc_span::{Span, Symbol}; use std::env; -macro_rules! log_capture_analysis { - ($fcx:expr, $closure_def_id:expr, $fmt:literal) => { - if $fcx.should_log_capture_analysis($closure_def_id) { - print!("For closure={:?}: ", $closure_def_id); - println!($fmt); - } - }; - - ($fcx:expr, $closure_def_id:expr, $fmt:literal, $($args:expr),*) => { - if $fcx.should_log_capture_analysis($closure_def_id) { - print!("For closure={:?}: ", $closure_def_id); - println!($fmt, $($args),*); - } - }; -} - /// Describe the relationship between the paths of two places /// eg: /// - foo is ancestor of foo.bar.baz @@ -144,9 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) { - log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis"); } else { - log_capture_analysis!(self, closure_def_id, "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); @@ -182,12 +164,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); - log_capture_analysis!( - self, - closure_def_id, - "capture information: {:#?}", - delegate.capture_information + debug!( + "For closure={:?}, capture_information={:#?}", + closure_def_id, delegate.capture_information ); + self.log_closure_capture_info(closure_def_id, &delegate.capture_information, span); if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure @@ -206,6 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.compute_min_captures(closure_def_id, delegate); + self.log_closure_min_capture_info(closure_def_id, span); + self.set_closure_captures(closure_def_id); // Now that we've analyzed the closure, we know how each @@ -333,10 +316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - debug!( - "For closure_def_id={:?}, set_closure_captures={:#?}", - closure_def_id, closure_captures - ); + debug!("For closure_def_id={:?}, closure_captures={:#?}", closure_def_id, closure_captures); debug!( "For closure_def_id={:?}, upvar_capture_map={:#?}", closure_def_id, upvar_capture_map @@ -478,12 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - log_capture_analysis!( - self, - closure_def_id, - "min_captures={:#?}", - root_var_min_capture_list - ); + debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); if !root_var_min_capture_list.is_empty() { self.typeck_results @@ -581,6 +556,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + + fn log_closure_capture_info( + &self, + closure_def_id: rustc_hir::def_id::DefId, + capture_information: &FxIndexMap, ty::CaptureInfo<'tcx>>, + closure_span: Span, + ) { + if self.should_log_capture_analysis(closure_def_id) { + for (place, capture_info) in capture_information { + let capture_str = construct_capture_info_string(self.tcx, place, capture_info); + let output_str = format!("Capturing {}", capture_str); + + let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + self.tcx.sess.span_err(span, &output_str); + } + } + } + + fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span) { + if self.should_log_capture_analysis(closure_def_id) { + if let Some(min_captures) = + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) + { + for (_, min_captures_for_var) in min_captures { + for capture in min_captures_for_var { + let place = &capture.place; + let capture_info = &capture.info; + + let capture_str = + construct_capture_info_string(self.tcx, place, capture_info); + let output_str = format!("Min Capture {}", capture_str); + + let span = + capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + self.tcx.sess.span_err(span, &output_str); + } + } + } + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -917,6 +932,37 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } } +fn construct_capture_info_string( + tcx: TyCtxt<'_>, + place: &Place<'tcx>, + capture_info: &ty::CaptureInfo<'tcx>, +) -> String { + let variable_name = match place.base { + PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), + _ => bug!("Capture_information should only contain upvars"), + }; + + let mut projections_str = String::new(); + for (i, item) in place.projections.iter().enumerate() { + let proj = match item.kind { + ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b), + ProjectionKind::Deref => String::from("Deref"), + ProjectionKind::Index => String::from("Index"), + ProjectionKind::Subslice => String::from("Subslice"), + }; + if i != 0 { + projections_str.push_str(","); + } + projections_str.push_str(proj.as_str()); + } + + let capture_kind_str = match capture_info.capture_kind { + ty::UpvarCapture::ByValue(_) => "ByValue".into(), + ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + }; + format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str) +} + fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index a5dd202bc072b..e144cce54ec22 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -10,6 +10,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { m[0] += 10; + //~^ ERROR: Capturing m[] -> MutBorrow + //~^^ ERROR: Min Capture m[] -> MutBorrow m[1] += 40; }; diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr index 3d3912d0796ee..228682caad78c 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:12:9 + | +LL | m[0] += 10; + | ^ + +error: Min Capture m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:12:9 + | +LL | m[0] += 10; + | ^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout deleted file mode 100644 index e0cd06f765e07..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout +++ /dev/null @@ -1,47 +0,0 @@ -For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): capture information: { - Place { - base_ty: [i32; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), - local_id: 12, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#6r), - ), - }, -} -For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: [i32; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), - local_id: 12, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#6r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index a2be21cddb48c..3f2e16725dc38 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -16,6 +16,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", p.x); + //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow }; // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr index 9233597c360d2..41e641f356418 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:18:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:18:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout deleted file mode 100644 index d378b12a23500..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), - local_id: 31, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), - local_id: 31, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index e06cde73158ea..5db1c9c0683ec 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -11,6 +11,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", t.0); + //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture t[(0, 0)] -> ImmBorrow }; // `c` only captures t.0, therefore mutating t.1 is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr index f83487ecce555..47470bb9646ea 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-disjoint-field-tuple.rs:8:13 + --> $DIR/capture-disjoint-field-tuple.rs:10:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/capture-disjoint-field-tuple.rs:1:12 + --> $DIR/capture-disjoint-field-tuple.rs:3:12 | LL | #![feature(capture_disjoint_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:13:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:13:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout deleted file mode 100644 index 9a080c28d4ebf..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): capture information: { - Place { - base_ty: (i32, i32), - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), - local_id: 28, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: (i32, i32), - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), - local_id: 28, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index 072ed8eeab6f2..3713abe52df20 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -9,5 +9,7 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("This uses new capture analyysis to capture s={}", s); + //~^ ERROR: Capturing s[] -> ImmBorrow + //~^^ ERROR: Min Capture s[] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr index 133de1d13e85d..6d4d7ee5fbeb4 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: Min Capture s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout deleted file mode 100644 index 69722f9d21ec2..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ /dev/null @@ -1,47 +0,0 @@ -For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): capture information: { - Place { - base_ty: std::string::String, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), - local_id: 52, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#50r), - ), - }, -} -For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: std::string::String, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), - local_id: 52, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#50r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs index aa251c4526cf3..3ef8b4acf9782 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -22,9 +22,10 @@ impl Data { // The closure passed to filter only captures self.filter, // therefore mutating self.list is allowed. self.list.retain( - //~^ cannot borrow `self.list` as mutable because it is also borrowed as immutable #[rustc_capture_analysis] |v| self.filter.allowed(*v), + //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow ); } } diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr index 3eb4decdeae29..452ba5c654516 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -1,5 +1,5 @@ warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/filter-on-struct-member.rs:1:12 + --> $DIR/filter-on-struct-member.rs:3:12 | LL | #![feature(capture_disjoint_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,22 +7,17 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error[E0502]: cannot borrow `self.list` as mutable because it is also borrowed as immutable - --> $DIR/filter-on-struct-member.rs:22:9 +error: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:26:17 | -LL | self.list.retain( - | ^ ------ immutable borrow later used by call - | _________| - | | -LL | | -LL | | #[rustc_capture_analysis] -LL | | |v| self.filter.allowed(*v), - | | --- ---- first borrow occurs due to use of `self` in closure - | | | - | | immutable borrow occurs here -LL | | ); - | |_________^ mutable borrow occurs here +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ -error: aborting due to previous error; 1 warning emitted +error: Min Capture self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:26:17 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout deleted file mode 100644 index 44fb88e1520c4..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout +++ /dev/null @@ -1,71 +0,0 @@ -For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): Using new-style capture analysis -For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): capture information: { - Place { - base_ty: &mut Data, - base: Upvar( - UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), - ), - projections: [ - Projection { - ty: Data, - kind: Deref, - }, - Projection { - ty: Filter, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), - local_id: 13, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#7r), - ), - }, -} -For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): min_captures={ - HirId { - owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: &mut Data, - base: Upvar( - UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), - ), - projections: [ - Projection { - ty: Data, - kind: Deref, - }, - Projection { - ty: Filter, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), - local_id: 13, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#7r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs index bd8d52d6a3c7e..a6793a43488e8 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -23,6 +23,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { let wp = &w.p; + //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture w[(0, 0)] -> ImmBorrow println!("{}", wp.x); }; diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr index bd339a68fa0c6..e8368201ede91 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:25:19 + | +LL | let wp = &w.p; + | ^^^ + +error: Min Capture w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:25:19 + | +LL | let wp = &w.p; + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout deleted file mode 100644 index 63a3669d0a76d..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_1[317d]::main), - local_id: 20, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#37r), - ), - }, -} -For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:8 ~ multilevel_path_1[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_1[317d]::main), - local_id: 20, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#37r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs index a8aca53bc73fd..994e2530f7825 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -20,6 +20,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", w.p.x); + //~^ ERROR: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow }; // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr index 772dfd643eaa0..dfcd2c7a08874 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:22:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:22:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout deleted file mode 100644 index 576d2c36b6f25..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout +++ /dev/null @@ -1,77 +0,0 @@ -For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_2[317d]::main), - local_id: 35, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:8 ~ multilevel_path_2[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_2[317d]::main), - local_id: 35, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index 64b69af0f0c1c..8641d6b4f59fb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -22,10 +22,18 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", p.x); + //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow let incr = 10; let mut c2 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental || p.y += incr; + //~^ ERROR: Capturing p[(1, 0)] -> MutBorrow + //~^^ ERROR: Capturing incr[] -> ImmBorrow + //~^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~^^^^ ERROR: Min Capture incr[] -> ImmBorrow + //~^^^^^ ERROR: Capturing p[(1, 0)] -> MutBorrow + //~^^^^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); }; diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr index dbd9e3655a323..2368a450bc6fb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:19:18 + --> $DIR/nested-closure.rs:21:18 | LL | let mut c1 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let mut c1 = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:24:22 + --> $DIR/nested-closure.rs:28:22 | LL | let mut c2 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let mut c2 = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/nested-closure.rs:1:12 + --> $DIR/nested-closure.rs:3:12 | LL | #![feature(capture_disjoint_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,6 +25,54 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to 2 previous errors; 1 warning emitted +error: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:30:19 + | +LL | || p.y += incr; + | ^^^^ + +error: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: Min Capture incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:30:19 + | +LL | || p.y += incr; + | ^^^^ + +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:24:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:24:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: aborting due to 10 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout deleted file mode 100644 index 446419c75f5eb..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout +++ /dev/null @@ -1,221 +0,0 @@ -For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): Using new-style capture analysis -For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#109r), - ), - }, - Place { - base_ty: i32, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 72, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#110r), - ), - }, -} -For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): min_captures={ - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#109r), - ), - }, - }, - ], - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 5, - }: [ - CapturedPlace { - place: Place { - base_ty: i32, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 72, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#110r), - ), - }, - }, - ], -} -For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 37, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#114r), - ), - }, - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#115r), - ), - }, -} -For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 37, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#114r), - ), - }, - }, - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#115r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs index c967c0b72d409..48e0698b4a750 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -24,5 +24,7 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", pent.points[5].x); + //~^ ERROR: Capturing pent[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture pent[(0, 0)] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr index 84bcc55d99c97..7507e550adeb8 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:26:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: Min Capture pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:26:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout deleted file mode 100644 index 73f880c155afd..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Pentagon, - base: Upvar( - UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: [Point; 5], - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:9 ~ path_with_array_access[317d]::main), - local_id: 83, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#34r), - ), - }, -} -For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:9 ~ path_with_array_access[317d]::main), - local_id: 6, - }: [ - CapturedPlace { - place: Place { - base_ty: Pentagon, - base: Upvar( - UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: [Point; 5], - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:9 ~ path_with_array_access[317d]::main), - local_id: 83, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#34r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs index 27ab9d6b7359e..3d5a08344b72a 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -26,7 +26,10 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { p.x += 10; + //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow + //~^^ ERROR: Min Capture p[] -> MutBorrow println!("{:?}", p); + //~^ ERROR: Capturing p[] -> ImmBorrow }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr index 002d2aaab89a5..53cc681969ad0 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -16,6 +16,24 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:28:9 + | +LL | p.x += 10; + | ^^^ + +error: Capturing p[] -> ImmBorrow + --> $DIR/simple-struct-min-capture.rs:31:26 + | +LL | println!("{:?}", p); + | ^ + +error: Min Capture p[] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:28:9 + | +LL | p.x += 10; + | ^^^ + +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout deleted file mode 100644 index c0421852d93d8..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout +++ /dev/null @@ -1,72 +0,0 @@ -For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 15, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#34r), - ), - }, - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 35, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 15, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#34r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs index fc966c4193e99..4e30194b17e48 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs @@ -19,6 +19,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { let [a, b, .., e] = arr; + //~^ ERROR: Capturing arr[Index] -> ByValue + //~^^ ERROR: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); assert_eq!(e, "E"); diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr index 5c6b505d64fd3..2547322a8abf2 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing arr[Index] -> ByValue + --> $DIR/slice-pat.rs:21:33 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Min Capture arr[] -> ByValue + --> $DIR/slice-pat.rs:21:33 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout deleted file mode 100644 index 43c43015134ae..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout +++ /dev/null @@ -1,56 +0,0 @@ -For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information: { - Place { - base_ty: [std::string::String; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: std::string::String, - kind: Index, - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ slice_pat[317d]::main), - local_id: 179, - }, - ), - capture_kind: ByValue( - Some( - $DIR/slice-pat.rs:21:33: 21:36 (#0), - ), - ), - }, -} -For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ slice_pat[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: [std::string::String; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ slice_pat[317d]::main), - local_id: 179, - }, - ), - capture_kind: ByValue( - Some( - $DIR/slice-pat.rs:21:33: 21:36 (#0), - ), - ), - }, - }, - ], -} From fa381600dcf4a5279e3222b48b4a1363d71ac7a9 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 1 Nov 2020 16:39:47 -0500 Subject: [PATCH 08/16] Handle `let _ = x` patterns in closure liveness analysis --- compiler/rustc_passes/src/liveness.rs | 69 +++++++++++++++++++-------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 7288015e17029..debb873beb93b 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -317,10 +317,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { // swap in a new set of IR maps for this body let mut maps = IrMaps::new(self.tcx); let hir_id = maps.tcx.hir().body_owner(body.id()); - let def_id = maps.tcx.hir().local_def_id(hir_id); + let local_def_id = maps.tcx.hir().local_def_id(hir_id); + let def_id = local_def_id.to_def_id(); // Don't run unused pass for #[derive()] - if let Some(parent) = self.tcx.parent(def_id.to_def_id()) { + if let Some(parent) = self.tcx.parent(def_id) { if let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) { if self.tcx.has_attr(parent, sym::automatically_derived) { return; @@ -328,8 +329,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } } - if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) { - for (&var_hir_id, _upvar) in upvars { + if let Some(captures) = maps.tcx.typeck(local_def_id).closure_captures.get(&def_id) { + for &var_hir_id in captures.keys() { let var_name = maps.tcx.hir().name(var_hir_id); maps.add_variable(Upvar(var_hir_id, var_name)); } @@ -340,7 +341,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { intravisit::walk_body(&mut maps, body); // compute liveness - let mut lsets = Liveness::new(&mut maps, def_id); + let mut lsets = Liveness::new(&mut maps, local_def_id); let entry_ln = lsets.compute(&body, hir_id); lsets.log_liveness(entry_ln, body.id().hir_id); @@ -397,10 +398,18 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { // construction site. let mut call_caps = Vec::new(); let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { + if let Some(captures) = self + .tcx + .typeck(closure_def_id) + .closure_captures + .get(&closure_def_id.to_def_id()) + { + // If closure captures is Some, upvars_mentioned must also be Some + let upvars = self.tcx.upvars_mentioned(closure_def_id).unwrap(); + call_caps.extend(captures.keys().map(|var_id| { + let upvar = upvars[var_id]; let upvar_ln = self.add_live_node(UpvarNode(upvar.span)); - CaptureInfo { ln: upvar_ln, var_hid: var_id } + CaptureInfo { ln: upvar_ln, var_hid: *var_id } })); } self.set_captures(expr.hir_id, call_caps); @@ -564,6 +573,7 @@ struct Liveness<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, upvars: Option<&'tcx FxIndexMap>, + closure_captures: Option<&'tcx FxIndexMap>, successors: IndexVec>, rwu_table: RWUTable, @@ -587,6 +597,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let typeck_results = ir.tcx.typeck(body_owner); let param_env = ir.tcx.param_env(body_owner); let upvars = ir.tcx.upvars_mentioned(body_owner); + let closure_captures = typeck_results.closure_captures.get(&body_owner.to_def_id()); let closure_ln = ir.add_live_node(ClosureNode); let exit_ln = ir.add_live_node(ExitNode); @@ -600,6 +611,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { typeck_results, param_env, upvars, + closure_captures, successors: IndexVec::from_elem_n(None, num_live_nodes), rwu_table: RWUTable::new(num_live_nodes * num_vars), closure_ln, @@ -850,14 +862,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - if let Some(upvars) = self.upvars { + if let Some(closure_captures) = self.closure_captures { // Mark upvars captured by reference as used after closure exits. - for (&var_hir_id, upvar) in upvars.iter().rev() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: self.body_owner, - }; - match self.typeck_results.upvar_capture(upvar_id) { + // Since closure_captures is Some, upvars must exists too. + let upvars = self.upvars.unwrap(); + for (&var_hir_id, upvar_id) in closure_captures { + let upvar = upvars[&var_hir_id]; + match self.typeck_results.upvar_capture(*upvar_id) { ty::UpvarCapture::ByRef(_) => { let var = self.variable(var_hir_id, upvar.span); self.acc(self.exit_ln, var, ACC_READ | ACC_USE); @@ -869,7 +880,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let succ = self.propagate_through_expr(&body.value, self.exit_ln); - if self.upvars.is_none() { + if self.closure_captures.is_none() { // Either not a closure, or closure without any captured variables. // No need to determine liveness of captured variables, since there // are none. @@ -1341,7 +1352,21 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { acc: u32, ) -> LiveNode { match path.res { - Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span), + Res::Local(hid) => { + let in_upvars = self.upvars.map_or(false, |u| u.contains_key(&hid)); + let in_captures = self.closure_captures.map_or(false, |c| c.contains_key(&hid)); + + match (in_upvars, in_captures) { + (false, _) | (true, true) => self.access_var(hir_id, hid, succ, acc, path.span), + (true, false) => { + // This case is possible when with RFC-2229, a wild pattern + // is used within a closure. + // eg: `let _ = x`. The closure doesn't capture x here, + // even though it's mentioned in the closure. + succ + } + } + } _ => succ, } } @@ -1531,11 +1556,15 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let upvars = match self.upvars { + let closure_captures = match self.closure_captures { None => return, - Some(upvars) => upvars, + Some(closure_captures) => closure_captures, }; - for (&var_hir_id, upvar) in upvars.iter() { + + // If closure_captures is Some(), upvars must be Some() too. + let upvars = self.upvars.unwrap(); + for &var_hir_id in closure_captures.keys() { + let upvar = upvars[&var_hir_id]; let var = self.variable(var_hir_id, upvar.span); let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, From be77402ee3ca6a4cab16b45c8c35baa13a9d5a05 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Thu, 5 Nov 2020 15:16:24 -0500 Subject: [PATCH 09/16] More pattern testcases --- .../destructure_patterns.rs | 66 +++++++++++ .../destructure_patterns.stderr | 111 ++++++++++++++++++ .../2229_closure_analysis/slice-pat.rs | 30 ----- .../2229_closure_analysis/slice-pat.stderr | 33 ------ .../2229_closure_analysis/wild_patterns.rs | 62 ++++++++++ .../wild_patterns.stderr | 75 ++++++++++++ 6 files changed, 314 insertions(+), 63 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr delete mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.rs delete mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/wild_patterns.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs new file mode 100644 index 0000000000000..36f258f950f4f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -0,0 +1,66 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure Index projections are handled properly during capture analysis +// The array should be moved in entirety, even though only some elements are used. +fn arrays() { + let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let [a, b, .., e] = arr; + //~^ ERROR: Capturing arr[Index] -> ByValue + //~^^ ERROR: Min Capture arr[] -> ByValue + assert_eq!(a, "A"); + assert_eq!(b, "B"); + assert_eq!(e, "E"); + }; + + c(); +} + +struct Point { + x: i32, + y: i32, + id: String, +} + +fn structs() { + let mut p = Point { x: 10, y: 10, id: String::new() }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let Point { x: ref mut x, y: _, id: moved_id } = p; + //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow + //~^^ ERROR: Capturing p[(2, 0)] -> ByValue + //~^^^ ERROR: Min Capture p[(0, 0)] -> MutBorrow + //~^^^^ ERROR: Min Capture p[(2, 0)] -> ByValue + + println!("{}, {}", x, moved_id); + }; + c(); +} + +fn tuples() { + let mut t = (10, String::new(), (String::new(), 42)); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let (ref mut x, ref ref_str, (moved_s, _)) = t; + //~^ ERROR: Capturing t[(0, 0)] -> MutBorrow + //~^^ ERROR: Capturing t[(1, 0)] -> ImmBorrow + //~^^^ ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue + //~^^^^ ERROR: Min Capture t[(0, 0)] -> MutBorrow + //~^^^^^ ERROR: Min Capture t[(1, 0)] -> ImmBorrow + //~^^^^^^ ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue + + println!("{}, {} {}", x, ref_str, moved_s); + }; + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr new file mode 100644 index 0000000000000..388cfd3da92c8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -0,0 +1,111 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:10:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:33:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:50:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/destructure_patterns.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:13:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Min Capture arr[] -> ByValue + --> $DIR/destructure_patterns.rs:13:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Capturing p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Min Capture p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Min Capture p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Capturing t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Capturing t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: aborting due to 15 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs deleted file mode 100644 index 4e30194b17e48..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete -#![feature(rustc_attrs)] - -// Test to ensure Index projections are handled properly during capture analysis -// -// The array should be moved in entirety, even though only some elements are used. - -fn main() { - let arr : [String; 5] = [ - format!("A"), - format!("B"), - format!("C"), - format!("D"), - format!("E") - ]; - - let c = #[rustc_capture_analysis] - //~^ ERROR: attributes on expressions are experimental - || { - let [a, b, .., e] = arr; - //~^ ERROR: Capturing arr[Index] -> ByValue - //~^^ ERROR: Min Capture arr[] -> ByValue - assert_eq!(a, "A"); - assert_eq!(b, "B"); - assert_eq!(e, "E"); - }; - - c(); -} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr deleted file mode 100644 index 2547322a8abf2..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0658]: attributes on expressions are experimental - --> $DIR/slice-pat.rs:18:13 - | -LL | let c = #[rustc_capture_analysis] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #15701 for more information - = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable - -warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/slice-pat.rs:1:12 - | -LL | #![feature(capture_disjoint_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53488 for more information - -error: Capturing arr[Index] -> ByValue - --> $DIR/slice-pat.rs:21:33 - | -LL | let [a, b, .., e] = arr; - | ^^^ - -error: Min Capture arr[] -> ByValue - --> $DIR/slice-pat.rs:21:33 - | -LL | let [a, b, .., e] = arr; - | ^^^ - -error: aborting due to 3 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs new file mode 100644 index 0000000000000..d17af7cc79a47 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -0,0 +1,62 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure that we can handle cases where +// let statements create no bindings are intialized +// using a Place expression +// +// Note: Currently when feature `capture_disjoint_fields` is enabled +// we can't handle such cases. So the test so the test + +struct Point { + x: i32, + y: i32, +} + +fn wild_struct() { + let p = Point { x: 10, y: 20 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + // FIXME(arora-aman): Change `_x` to `_` + let Point { x: _x, y: _ } = p; + //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + }; + + c(); +} + +fn wild_tuple() { + let t = (String::new(), 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + // FIXME(arora-aman): Change `_x` to `_` + let (_x, _) = t; + //~^ ERROR: Capturing t[(0, 0)] -> ByValue + //~^^ ERROR: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +fn wild_arr() { + let arr = [String::new(), String::new()]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + // FIXME(arora-aman): Change `_x` to `_` + let [_x, _] = arr; + //~^ ERROR: Capturing arr[Index] -> ByValue + //~^^ ERROR: Min Capture arr[] -> ByValue + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr new file mode 100644 index 0000000000000..621c8aeb790f9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr @@ -0,0 +1,75 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:20:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:35:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:50:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/wild_patterns.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:24:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:24:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: Capturing t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:39:23 + | +LL | let (_x, _) = t; + | ^ + +error: Min Capture t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:39:23 + | +LL | let (_x, _) = t; + | ^ + +error: Capturing arr[Index] -> ByValue + --> $DIR/wild_patterns.rs:54:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: Min Capture arr[] -> ByValue + --> $DIR/wild_patterns.rs:54:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: aborting due to 9 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. From abc40040be3e10bb8228d627012c92c54d8f791f Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Thu, 5 Nov 2020 15:36:27 -0500 Subject: [PATCH 10/16] Remove local testing env var --- compiler/rustc_typeck/src/check/upvar.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index a01aa8531ca9c..2dff7d0fbddfb 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -44,8 +44,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::sym; use rustc_span::{Span, Symbol}; -use std::env; - /// Describe the relationship between the paths of two places /// eg: /// - foo is ancestor of foo.bar.baz @@ -127,8 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); - if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) { - } else { + if !self.tcx.features().capture_disjoint_fields { 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); From 43423f67a0f260c41181c5b2c1d25757f6b6babe Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 9 Nov 2020 00:15:45 -0500 Subject: [PATCH 11/16] Address review comments --- compiler/rustc_middle/src/ty/context.rs | 5 +- compiler/rustc_middle/src/ty/mod.rs | 35 +++-- compiler/rustc_typeck/src/check/upvar.rs | 176 +++++++++++++---------- 3 files changed, 131 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 76ca0c51ce1c3..ee577213aba14 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,9 +415,8 @@ pub struct TypeckResults<'tcx> { /// entire variable. pub closure_captures: ty::UpvarListMap, - /// Given the closure DefId this map provides a map of - /// root variables to minimum set of `Place`s (and how) that need to be tracked - /// to support all captures of that closure. + /// Tracks the minimum captures required for a closure; + /// see `MinCaptureInformationMap` for more details. pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>, /// Stores the type, expression, span and optional scope span of all types diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c41463d18448d..0c786be0478de 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -763,6 +763,31 @@ pub struct UpvarBorrow<'tcx> { pub region: ty::Region<'tcx>, } +/// Given the closure DefId this map provides a map of root variables to minimum +/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure. +pub type MinCaptureInformationMap<'tcx> = FxHashMap>; + +/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. +/// Used to track the minimum set of `Place`s that need to be captured to support all +/// Places captured by the closure starting at a given root variable. +/// +/// This provides a convenient and quick way of checking if a variable being used within +/// a closure is a capture of a local variable. +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; + +/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. +pub type MinCaptureList<'tcx> = Vec>; + +/// A `Place` and the corresponding `CaptureInfo`. +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct CapturedPlace<'tcx> { + pub place: HirPlace<'tcx>, + pub info: CaptureInfo<'tcx>, +} + +/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) +/// for a particular capture as well as identifying the part of the source code +/// that triggered this capture to occur. #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] pub struct CaptureInfo<'tcx> { /// Expr Id pointing to use that resulted in selecting the current capture kind @@ -788,19 +813,9 @@ pub struct CaptureInfo<'tcx> { pub capture_kind: UpvarCapture<'tcx>, } -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct CapturedPlace<'tcx> { - pub place: HirPlace<'tcx>, - pub info: CaptureInfo<'tcx>, -} - pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; -pub type MinCaptureList<'tcx> = Vec>; -pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; -pub type MinCaptureInformationMap<'tcx> = FxHashMap>; - #[derive(Clone, Copy, PartialEq, Eq)] pub enum IntVarValue { IntType(ast::IntTy), diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 2dff7d0fbddfb..90e021ae592a5 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -46,9 +46,9 @@ use rustc_span::{Span, Symbol}; /// Describe the relationship between the paths of two places /// eg: -/// - foo is ancestor of foo.bar.baz -/// - foo.bar.baz is an descendant of foo.bar, -/// - foo.bar and foo.baz are divergent +/// - `foo` is ancestor of `foo.bar.baz` +/// - `foo.bar.baz` is an descendant of `foo.bar` +/// - `foo.bar` and `foo.baz` are divergent enum PlaceAncestryRelation { Ancestor, Descendant, @@ -124,7 +124,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); - let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); + let mut capture_information: FxIndexMap, ty::CaptureInfo<'tcx>> = + Default::default(); if !self.tcx.features().capture_disjoint_fields { if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { for (&var_hir_id, _) in upvars.iter() { @@ -186,7 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.compute_min_captures(closure_def_id, delegate); self.log_closure_min_capture_info(closure_def_id, span); - self.set_closure_captures(closure_def_id); + self.min_captures_to_closure_captures_bridge(closure_def_id); // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure @@ -274,8 +275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// 2. upvar_capture_map /// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue - - fn set_closure_captures(&self, closure_def_id: DefId) { + fn min_captures_to_closure_captures_bridge(&self, closure_def_id: DefId) { let mut closure_captures: FxIndexMap = Default::default(); let mut upvar_capture_map = ty::UpvarCaptureMap::default(); @@ -304,8 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we create a fake capture info with no expression. let fake_capture_info = ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; - self.determine_capture_info(fake_capture_info, capture_info.clone()) - .capture_kind + determine_capture_info(fake_capture_info, capture_info).capture_kind } else { capture_info.capture_kind }; @@ -329,7 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Analyses the information collected by InferBorrowKind to compute the min number of + /// Analyzes the information collected by `InferBorrowKind` to compute the min number of /// Places (and corresponding capture kind) that we need to keep track of to support all /// the required captured paths. /// @@ -420,8 +419,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // current place is ancestor of possible_descendant PlaceAncestryRelation::Ancestor => { descendant_found = true; - updated_capture_info = self - .determine_capture_info(updated_capture_info, possible_descendant.info); + updated_capture_info = + determine_capture_info(updated_capture_info, possible_descendant.info); false } @@ -437,7 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceAncestryRelation::Descendant => { ancestor_found = true; possible_ancestor.info = - self.determine_capture_info(possible_ancestor.info, capture_info); + determine_capture_info(possible_ancestor.info, capture_info); // Only one ancestor of the current place will be in the list. break; @@ -500,60 +499,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } - /// Helper function to determine if we need to escalate CaptureKind from - /// CaptureInfo A to B and returns the escalated CaptureInfo. - /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) - /// - /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based - /// on the `CaptureInfo` containing an associated expression id. - /// - /// If both the CaptureKind and Expression are considered to be equivalent, - /// then `CaptureInfo` A is preferred. - fn determine_capture_info( - &self, - capture_info_a: ty::CaptureInfo<'tcx>, - capture_info_b: ty::CaptureInfo<'tcx>, - ) -> ty::CaptureInfo<'tcx> { - // If the capture kind is equivalent then, we don't need to escalate and can compare the - // expressions. - let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => true, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - ref_a.kind == ref_b.kind - } - _ => false, - }; - - if eq_capture_kind { - match (capture_info_a.expr_id, capture_info_b.expr_id) { - (Some(_), _) | (None, None) => capture_info_a, - (None, Some(_)) => capture_info_b, - } - } else { - match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), _) => capture_info_a, - (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - match (ref_a.kind, ref_b.kind) { - // Take LHS: - (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) - | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, - - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, - - (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) - | (ty::MutBorrow, ty::MutBorrow) => { - bug!("Expected unequal capture kinds"); - } - } - } - } - } - } - fn log_closure_capture_info( &self, closure_def_id: rustc_hir::def_id::DefId, @@ -617,8 +562,9 @@ struct InferBorrowKind<'a, 'tcx> { // variable access that caused us to do so. current_origin: Option<(Span, Symbol)>, - /// For each Place that we access, we track the minimal kind of + /// For each Place that is captured by the closure, we track the minimal kind of /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. + /// /// Consider closure where s.str1 is captured via an ImmutableBorrow and /// s.str2 via a MutableBorrow /// @@ -686,7 +632,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { }; let curr_info = self.capture_information[&place_with_id.place]; - let updated_info = self.fcx.determine_capture_info(curr_info, capture_info); + let updated_info = determine_capture_info(curr_info, capture_info); self.capture_information[&place_with_id.place] = updated_info; } @@ -804,7 +750,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { expr_id: Some(diag_expr_id), capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), }; - let updated_info = self.fcx.determine_capture_info(curr_capture_info, capture_info); + let updated_info = determine_capture_info(curr_capture_info, capture_info); self.capture_information[&place_with_id.place] = updated_info; }; } @@ -859,14 +805,14 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { 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 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 }; + debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info); + self.capture_information.insert(place_with_id.place.clone(), capture_info); } else { debug!("Not upvar: {:?}", place_with_id); @@ -964,6 +910,92 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } +/// Helper function to determine if we need to escalate CaptureKind from +/// CaptureInfo A to B and returns the escalated CaptureInfo. +/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) +/// +/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based +/// on the `CaptureInfo` containing an associated expression id. +/// +/// If both the CaptureKind and Expression are considered to be equivalent, +/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize +/// expressions reported back to the user as part of diagnostics based on which appears earlier +/// in the closure. This can be acheived simply by calling +/// `determine_capture_info(existing_info, current_info)`. This works out because the +/// expressions that occur earlier in the closure body than the current expression are processed before. +/// Consider the following example +/// ```rust +/// let mut p: Point { x: 10, y: 10 }; +/// +/// let c = || { +/// p.x += 10; +/// // ^ E1 ^ +/// // ... +/// // More code +/// // ... +/// p.x += 10; // E2 +/// // ^ E2 ^ +/// } +/// ``` +/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), +/// and both have an expression associated, however for diagnostics we prfer reporting +/// `E1` since it appears earlier in the closure body. When `E2` is being processed we +/// would've already handled `E1`, and have an existing capture_information for it. +/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return +/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. +fn determine_capture_info( + capture_info_a: ty::CaptureInfo<'tcx>, + capture_info_b: ty::CaptureInfo<'tcx>, +) -> ty::CaptureInfo<'tcx> { + // If the capture kind is equivalent then, we don't need to escalate and can compare the + // expressions. + let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { + // We don't need to worry about the spans being ignored here. + // + // The expr_id in capture_info corresponds to the span that is stored within + // ByValue(span) and therefore it gets handled with priortizing based on + // expressions below. + true + } + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + ref_a.kind == ref_b.kind + } + (ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false, + }; + + if eq_capture_kind { + match (capture_info_a.expr_id, capture_info_b.expr_id) { + (Some(_), _) | (None, None) => capture_info_a, + (None, Some(_)) => capture_info_b, + } + } else { + // We select the CaptureKind which ranks higher based the following priority order: + // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow + match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), _) => capture_info_a, + (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + match (ref_a.kind, ref_b.kind) { + // Take LHS: + (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) + | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, + + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, + + (ty::ImmBorrow, ty::ImmBorrow) + | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) + | (ty::MutBorrow, ty::MutBorrow) => { + bug!("Expected unequal capture kinds"); + } + } + } + } + } +} + /// Determines the Ancestry relationship of Place A relative to Place B /// /// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B From deeb025f39a39fc58fc87900d62550dada849e1b Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 10 Nov 2020 03:05:50 -0500 Subject: [PATCH 12/16] Address review comments 2 --- compiler/rustc_typeck/src/check/upvar.rs | 2 +- .../arrays-completely-captured.rs | 2 +- .../capture-disjoint-field-struct.rs | 2 +- .../capture-disjoint-field-tuple.rs | 2 +- .../destructure_patterns.rs | 18 +++++++++--------- .../feature-gate-capture_disjoint_fields.rs | 2 +- .../filter-on-struct-member.rs | 2 +- .../2229_closure_analysis/multilevel-path-1.rs | 2 +- .../2229_closure_analysis/multilevel-path-2.rs | 2 +- .../2229_closure_analysis/nested-closure.rs | 12 ++++++------ .../path-with-array-access.rs | 2 +- .../simple-struct-min-capture.rs | 2 +- .../2229_closure_analysis/wild_patterns.rs | 6 +++--- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 90e021ae592a5..87365de90470d 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -938,7 +938,7 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { /// } /// ``` /// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), -/// and both have an expression associated, however for diagnostics we prfer reporting +/// and both have an expression associated, however for diagnostics we prefer reporting /// `E1` since it appears earlier in the closure body. When `E2` is being processed we /// would've already handled `E1`, and have an existing capture_information for it. /// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index e144cce54ec22..01c28aa29fb8e 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -11,7 +11,7 @@ fn main() { || { m[0] += 10; //~^ ERROR: Capturing m[] -> MutBorrow - //~^^ ERROR: Min Capture m[] -> MutBorrow + //~| ERROR: Min Capture m[] -> MutBorrow m[1] += 40; }; diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index 3f2e16725dc38..b50c2d66d94b4 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -17,7 +17,7 @@ fn main() { || { println!("{}", p.x); //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow }; // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index 5db1c9c0683ec..99095c7198663 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -12,7 +12,7 @@ fn main() { || { println!("{}", t.0); //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture t[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture t[(0, 0)] -> ImmBorrow }; // `c` only captures t.0, therefore mutating t.1 is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs index 36f258f950f4f..eef8fd6e55748 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -12,7 +12,7 @@ fn arrays() { || { let [a, b, .., e] = arr; //~^ ERROR: Capturing arr[Index] -> ByValue - //~^^ ERROR: Min Capture arr[] -> ByValue + //~| ERROR: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); assert_eq!(e, "E"); @@ -35,9 +35,9 @@ fn structs() { || { let Point { x: ref mut x, y: _, id: moved_id } = p; //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~^^ ERROR: Capturing p[(2, 0)] -> ByValue - //~^^^ ERROR: Min Capture p[(0, 0)] -> MutBorrow - //~^^^^ ERROR: Min Capture p[(2, 0)] -> ByValue + //~| ERROR: Capturing p[(2, 0)] -> ByValue + //~| ERROR: Min Capture p[(0, 0)] -> MutBorrow + //~| ERROR: Min Capture p[(2, 0)] -> ByValue println!("{}, {}", x, moved_id); }; @@ -52,11 +52,11 @@ fn tuples() { || { let (ref mut x, ref ref_str, (moved_s, _)) = t; //~^ ERROR: Capturing t[(0, 0)] -> MutBorrow - //~^^ ERROR: Capturing t[(1, 0)] -> ImmBorrow - //~^^^ ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue - //~^^^^ ERROR: Min Capture t[(0, 0)] -> MutBorrow - //~^^^^^ ERROR: Min Capture t[(1, 0)] -> ImmBorrow - //~^^^^^^ ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue + //~| ERROR: Capturing t[(1, 0)] -> ImmBorrow + //~| ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue + //~| ERROR: Min Capture t[(0, 0)] -> MutBorrow + //~| ERROR: Min Capture t[(1, 0)] -> ImmBorrow + //~| ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue println!("{}, {} {}", x, ref_str, moved_s); }; diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index 3713abe52df20..ee55e3a3f2181 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -10,6 +10,6 @@ fn main() { || { println!("This uses new capture analyysis to capture s={}", s); //~^ ERROR: Capturing s[] -> ImmBorrow - //~^^ ERROR: Min Capture s[] -> ImmBorrow + //~| ERROR: Min Capture s[] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs index 3ef8b4acf9782..d526934271c4b 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -25,7 +25,7 @@ impl Data { #[rustc_capture_analysis] |v| self.filter.allowed(*v), //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow ); } } diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs index a6793a43488e8..08c9aa8eff849 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -24,7 +24,7 @@ fn main() { || { let wp = &w.p; //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture w[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture w[(0, 0)] -> ImmBorrow println!("{}", wp.x); }; diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs index 994e2530f7825..020753a71bf3f 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -21,7 +21,7 @@ fn main() { || { println!("{}", w.p.x); //~^ ERROR: Capturing w[(0, 0),(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow }; // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index 8641d6b4f59fb..d2e99fe4accf6 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -23,17 +23,17 @@ fn main() { || { println!("{}", p.x); //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow let incr = 10; let mut c2 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental || p.y += incr; //~^ ERROR: Capturing p[(1, 0)] -> MutBorrow - //~^^ ERROR: Capturing incr[] -> ImmBorrow - //~^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow - //~^^^^ ERROR: Min Capture incr[] -> ImmBorrow - //~^^^^^ ERROR: Capturing p[(1, 0)] -> MutBorrow - //~^^^^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~| ERROR: Capturing incr[] -> ImmBorrow + //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~| ERROR: Min Capture incr[] -> ImmBorrow + //~| ERROR: Capturing p[(1, 0)] -> MutBorrow + //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); }; diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs index 48e0698b4a750..4a42970137fff 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -25,6 +25,6 @@ fn main() { || { println!("{}", pent.points[5].x); //~^ ERROR: Capturing pent[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture pent[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture pent[(0, 0)] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs index 3d5a08344b72a..68c18eac8048e 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -27,7 +27,7 @@ fn main() { || { p.x += 10; //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~^^ ERROR: Min Capture p[] -> MutBorrow + //~| ERROR: Min Capture p[] -> MutBorrow println!("{:?}", p); //~^ ERROR: Capturing p[] -> ImmBorrow }; diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs index d17af7cc79a47..889836c11ce88 100644 --- a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -23,7 +23,7 @@ fn wild_struct() { // FIXME(arora-aman): Change `_x` to `_` let Point { x: _x, y: _ } = p; //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow }; c(); @@ -38,7 +38,7 @@ fn wild_tuple() { // FIXME(arora-aman): Change `_x` to `_` let (_x, _) = t; //~^ ERROR: Capturing t[(0, 0)] -> ByValue - //~^^ ERROR: Min Capture t[(0, 0)] -> ByValue + //~| ERROR: Min Capture t[(0, 0)] -> ByValue }; c(); @@ -53,7 +53,7 @@ fn wild_arr() { // FIXME(arora-aman): Change `_x` to `_` let [_x, _] = arr; //~^ ERROR: Capturing arr[Index] -> ByValue - //~^^ ERROR: Min Capture arr[] -> ByValue + //~| ERROR: Min Capture arr[] -> ByValue }; c(); From d0fac05d8f0920d0aaa81dc4832334a49e7dbff8 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 10 Nov 2020 22:54:04 -0500 Subject: [PATCH 13/16] Add test for capturing enums --- .../2229_closure_analysis/capture-enums.rs | 56 +++++++++++++ .../capture-enums.stderr | 78 +++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-enums.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-enums.stderr diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs new file mode 100644 index 0000000000000..175fd79012446 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs @@ -0,0 +1,56 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +enum Info { + Point(i32, i32, String), + Meta(String, Vec<(i32, i32)>) +} + +fn multi_variant_enum() { + let point = Info::Point(10, -10, "1".into()); + + let vec = Vec::new(); + let meta = Info::Meta("meta".into(), vec); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + if let Info::Point(_, _, str) = point { + //~^ Capturing point[] -> ImmBorrow + //~| Capturing point[(2, 0)] -> ByValue + //~| Min Capture point[] -> ByValue + println!("{}", str); + } + + if let Info::Meta(_, v) = meta { + //~^ Capturing meta[] -> ImmBorrow + //~| Capturing meta[(1, 1)] -> ByValue + //~| Min Capture meta[] -> ByValue + println!("{:?}", v); + } + }; + + c(); +} + +enum SingleVariant { + Point(i32, i32, String), +} + +fn single_variant_enum() { + let point = SingleVariant::Point(10, -10, "1".into()); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let SingleVariant::Point(_, _, str) = point; + //~^ Capturing point[(2, 0)] -> ByValue + //~| Min Capture point[(2, 0)] -> ByValue + println!("{}", str); + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr new file mode 100644 index 0000000000000..76a2de2faf9c8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -0,0 +1,78 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:16:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:44:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-enums.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: Capturing point[] -> ImmBorrow + --> $DIR/capture-enums.rs:19:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ + +error: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:19:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ + +error: Capturing meta[] -> ImmBorrow + --> $DIR/capture-enums.rs:26:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Capturing meta[(1, 1)] -> ByValue + --> $DIR/capture-enums.rs:26:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Min Capture point[] -> ByValue + --> $DIR/capture-enums.rs:19:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ + +error: Min Capture meta[] -> ByValue + --> $DIR/capture-enums.rs:26:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:47:43 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: Min Capture point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:47:43 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: aborting due to 10 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. From c50e57f946ee5a73b50fa5c52bb7a2a8a0cecf3f Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Fri, 13 Nov 2020 01:51:19 -0500 Subject: [PATCH 14/16] Log closure as well --- compiler/rustc_typeck/src/check/upvar.rs | 15 +- .../arrays-completely-captured.rs | 11 +- .../arrays-completely-captured.stderr | 34 ++++- .../capture-disjoint-field-struct.rs | 11 +- .../capture-disjoint-field-struct.stderr | 34 ++++- .../capture-disjoint-field-tuple.rs | 11 +- .../capture-disjoint-field-tuple.stderr | 34 ++++- .../2229_closure_analysis/capture-enums.rs | 28 ++-- .../capture-enums.stderr | 98 +++++++++---- .../destructure_patterns.rs | 37 +++-- .../destructure_patterns.stderr | 134 +++++++++++++----- .../feature-gate-capture_disjoint_fields.rs | 11 +- ...eature-gate-capture_disjoint_fields.stderr | 34 ++++- .../filter-on-struct-member.rs | 10 +- .../filter-on-struct-member.stderr | 20 ++- .../multilevel-path-1.rs | 11 +- .../multilevel-path-1.stderr | 34 ++++- .../multilevel-path-2.rs | 11 +- .../multilevel-path-2.stderr | 34 ++++- .../2229_closure_analysis/nested-closure.rs | 26 ++-- .../nested-closure.stderr | 78 +++++++--- .../path-with-array-access.rs | 11 +- .../path-with-array-access.stderr | 34 ++++- .../simple-struct-min-capture.rs | 13 +- .../simple-struct-min-capture.stderr | 41 ++++-- .../2229_closure_analysis/wild_patterns.rs | 29 ++-- .../wild_patterns.stderr | 102 +++++++++++-- 27 files changed, 727 insertions(+), 219 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 87365de90470d..e0bbe3cb07917 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -166,7 +166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information ); - self.log_closure_capture_info(closure_def_id, &delegate.capture_information, span); + self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure @@ -499,20 +499,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } - fn log_closure_capture_info( + fn log_capture_analysis_first_pass( &self, closure_def_id: rustc_hir::def_id::DefId, capture_information: &FxIndexMap, ty::CaptureInfo<'tcx>>, closure_span: Span, ) { if self.should_log_capture_analysis(closure_def_id) { + let mut diag = + self.tcx.sess.struct_span_err(closure_span, "First Pass analysis includes:"); for (place, capture_info) in capture_information { let capture_str = construct_capture_info_string(self.tcx, place, capture_info); let output_str = format!("Capturing {}", capture_str); let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - self.tcx.sess.span_err(span, &output_str); + diag.span_note(span, &output_str); } + diag.emit(); } } @@ -521,6 +524,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(min_captures) = self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) { + let mut diag = + self.tcx.sess.struct_span_err(closure_span, "Min Capture analysis includes:"); + for (_, min_captures_for_var) in min_captures { for capture in min_captures_for_var { let place = &capture.place; @@ -532,9 +538,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - self.tcx.sess.span_err(span, &output_str); + diag.span_note(span, &output_str); } } + diag.emit(); } } } diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index 01c28aa29fb8e..131af6a10c898 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 #![feature(rustc_attrs)] // Ensure that capture analysis results in arrays being completely captured. @@ -8,10 +10,13 @@ fn main() { let mut c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: m[0] += 10; - //~^ ERROR: Capturing m[] -> MutBorrow - //~| ERROR: Min Capture m[] -> MutBorrow + //~^ NOTE: Capturing m[] -> MutBorrow + //~| NOTE: Min Capture m[] -> MutBorrow m[1] += 40; }; diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr index 228682caad78c..2a350f3033192 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/arrays-completely-captured.rs:9:17 + --> $DIR/arrays-completely-captured.rs:11:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing m[] -> MutBorrow - --> $DIR/arrays-completely-captured.rs:12:9 +error: First Pass analysis includes: + --> $DIR/arrays-completely-captured.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | m[1] += 40; +LL | | }; + | |_____^ + | +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 | LL | m[0] += 10; | ^ -error: Min Capture m[] -> MutBorrow - --> $DIR/arrays-completely-captured.rs:12:9 +error: Min Capture analysis includes: + --> $DIR/arrays-completely-captured.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | m[1] += 40; +LL | | }; + | |_____^ + | +note: Min Capture m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 | LL | m[0] += 10; | ^ diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index b50c2d66d94b4..ba4955085372a 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Point { @@ -14,10 +16,13 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: println!("{}", p.x); - //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow }; // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr index 41e641f356418..5fac6963afd32 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-disjoint-field-struct.rs:15:13 + --> $DIR/capture-disjoint-field-struct.rs:17:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-struct.rs:18:24 +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:23:24 | LL | println!("{}", p.x); | ^^^ -error: Min Capture p[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-struct.rs:18:24 +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:23:24 | LL | println!("{}", p.x); | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index 99095c7198663..c1693fbad7986 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] fn main() { @@ -9,10 +11,13 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: println!("{}", t.0); - //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture t[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow }; // `c` only captures t.0, therefore mutating t.1 is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr index 47470bb9646ea..1bfd63f2ace8c 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-disjoint-field-tuple.rs:10:13 + --> $DIR/capture-disjoint-field-tuple.rs:12:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing t[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-tuple.rs:13:24 +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:18:24 | LL | println!("{}", t.0); | ^^^ -error: Min Capture t[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-tuple.rs:13:24 +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:18:24 | LL | println!("{}", t.0); | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs index 175fd79012446..8fb2f7f16d69c 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] enum Info { @@ -15,18 +17,21 @@ fn multi_variant_enum() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: if let Info::Point(_, _, str) = point { - //~^ Capturing point[] -> ImmBorrow - //~| Capturing point[(2, 0)] -> ByValue - //~| Min Capture point[] -> ByValue + //~^ NOTE: Capturing point[] -> ImmBorrow + //~| NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[] -> ByValue println!("{}", str); } if let Info::Meta(_, v) = meta { - //~^ Capturing meta[] -> ImmBorrow - //~| Capturing meta[(1, 1)] -> ByValue - //~| Min Capture meta[] -> ByValue + //~^ NOTE: Capturing meta[] -> ImmBorrow + //~| NOTE: Capturing meta[(1, 1)] -> ByValue + //~| NOTE: Min Capture meta[] -> ByValue println!("{:?}", v); } }; @@ -43,10 +48,13 @@ fn single_variant_enum() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { - let SingleVariant::Point(_, _, str) = point; - //~^ Capturing point[(2, 0)] -> ByValue - //~| Min Capture point[(2, 0)] -> ByValue + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let SingleVariant::Point(_, _, str) = point; + //~^ NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[(2, 0)] -> ByValue println!("{}", str); }; diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr index 76a2de2faf9c8..ebe1dcb98848b 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-enums.rs:16:13 + --> $DIR/capture-enums.rs:18:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/capture-enums.rs:44:13 + --> $DIR/capture-enums.rs:49:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,54 +25,98 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing point[] -> ImmBorrow - --> $DIR/capture-enums.rs:19:41 +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing point[] -> ImmBorrow + --> $DIR/capture-enums.rs:24:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ - -error: Capturing point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:19:41 +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:24:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ - -error: Capturing meta[] -> ImmBorrow - --> $DIR/capture-enums.rs:26:35 +note: Capturing meta[] -> ImmBorrow + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ - -error: Capturing meta[(1, 1)] -> ByValue - --> $DIR/capture-enums.rs:26:35 +note: Capturing meta[(1, 1)] -> ByValue + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ -error: Min Capture point[] -> ByValue - --> $DIR/capture-enums.rs:19:41 +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture point[] -> ByValue + --> $DIR/capture-enums.rs:24:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ - -error: Min Capture meta[] -> ByValue - --> $DIR/capture-enums.rs:26:35 +note: Min Capture meta[] -> ByValue + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ -error: Capturing point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:47:43 +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ | -LL | let SingleVariant::Point(_, _, str) = point; - | ^^^^^ +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:55:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ -error: Min Capture point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:47:43 +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ + | +note: Min Capture point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:55:47 | -LL | let SingleVariant::Point(_, _, str) = point; - | ^^^^^ +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ -error: aborting due to 10 previous errors; 1 warning emitted +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs index eef8fd6e55748..080ca0405b477 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] // Test to ensure Index projections are handled properly during capture analysis @@ -9,10 +11,13 @@ fn arrays() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let [a, b, .., e] = arr; - //~^ ERROR: Capturing arr[Index] -> ByValue - //~| ERROR: Min Capture arr[] -> ByValue + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); assert_eq!(e, "E"); @@ -32,12 +37,15 @@ fn structs() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let Point { x: ref mut x, y: _, id: moved_id } = p; - //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~| ERROR: Capturing p[(2, 0)] -> ByValue - //~| ERROR: Min Capture p[(0, 0)] -> MutBorrow - //~| ERROR: Min Capture p[(2, 0)] -> ByValue + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Capturing p[(2, 0)] -> ByValue + //~| NOTE: Min Capture p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(2, 0)] -> ByValue println!("{}, {}", x, moved_id); }; @@ -49,14 +57,17 @@ fn tuples() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let (ref mut x, ref ref_str, (moved_s, _)) = t; - //~^ ERROR: Capturing t[(0, 0)] -> MutBorrow - //~| ERROR: Capturing t[(1, 0)] -> ImmBorrow - //~| ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue - //~| ERROR: Min Capture t[(0, 0)] -> MutBorrow - //~| ERROR: Min Capture t[(1, 0)] -> ImmBorrow - //~| ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0)] -> MutBorrow + //~| NOTE: Capturing t[(1, 0)] -> ImmBorrow + //~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue println!("{}, {} {}", x, ref_str, moved_s); }; diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr index 388cfd3da92c8..06ccc2d7a88b4 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:10:13 + --> $DIR/destructure_patterns.rs:12:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:33:13 + --> $DIR/destructure_patterns.rs:38:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:50:13 + --> $DIR/destructure_patterns.rs:58:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,78 +34,144 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing arr[Index] -> ByValue - --> $DIR/destructure_patterns.rs:13:29 +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:18:29 | LL | let [a, b, .., e] = arr; | ^^^ -error: Min Capture arr[] -> ByValue - --> $DIR/destructure_patterns.rs:13:29 +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/destructure_patterns.rs:18:29 | LL | let [a, b, .., e] = arr; | ^^^ -error: Capturing p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:36:58 +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ - -error: Capturing p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:36:58 +note: Capturing p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ -error: Min Capture p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:36:58 +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ - -error: Min Capture p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:36:58 +note: Min Capture p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ -error: Capturing t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:53:54 +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Capturing t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:53:54 +note: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Capturing t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:53:54 +note: Capturing t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ -error: Min Capture t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:53:54 +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Min Capture t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:53:54 +note: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Min Capture t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:53:54 +note: Min Capture t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ -error: aborting due to 15 previous errors; 1 warning emitted +error: aborting due to 9 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index ee55e3a3f2181..a3222635b626c 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] fn main() { @@ -7,9 +9,12 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("This uses new capture analyysis to capture s={}", s); - //~^ ERROR: Capturing s[] -> ImmBorrow - //~| ERROR: Min Capture s[] -> ImmBorrow + //~^ NOTE: Capturing s[] -> ImmBorrow + //~| NOTE: Min Capture s[] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr index 6d4d7ee5fbeb4..a031360ed34e1 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 + --> $DIR/feature-gate-capture_disjoint_fields.rs:10:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing s[] -> ImmBorrow - --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 +error: First Pass analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:16:69 | LL | println!("This uses new capture analyysis to capture s={}", s); | ^ -error: Min Capture s[] -> ImmBorrow - --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 +error: Min Capture analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:16:69 | LL | println!("This uses new capture analyysis to capture s={}", s); | ^ diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs index d526934271c4b..9466e103897fb 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Filter { @@ -24,8 +26,10 @@ impl Data { self.list.retain( #[rustc_capture_analysis] |v| self.filter.allowed(*v), - //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow ); } } diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr index 452ba5c654516..e9420fe5a0c3a 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -7,14 +7,26 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing self[Deref,(0, 0)] -> ImmBorrow - --> $DIR/filter-on-struct-member.rs:26:17 +error: First Pass analysis includes: + --> $DIR/filter-on-struct-member.rs:28:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:28:17 | LL | |v| self.filter.allowed(*v), | ^^^^^^^^^^^ -error: Min Capture self[Deref,(0, 0)] -> ImmBorrow - --> $DIR/filter-on-struct-member.rs:26:17 +error: Min Capture analysis includes: + --> $DIR/filter-on-struct-member.rs:28:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:28:17 | LL | |v| self.filter.allowed(*v), | ^^^^^^^^^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs index 08c9aa8eff849..7d2d4c104d489 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] #![allow(unused)] @@ -21,10 +23,13 @@ fn main() { // Note that `wp.x` doesn't start off a variable defined outside the closure. let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let wp = &w.p; - //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture w[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow println!("{}", wp.x); }; diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr index e8368201ede91..1c8db7952afe7 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/multilevel-path-1.rs:22:13 + --> $DIR/multilevel-path-1.rs:24:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing w[(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-1.rs:25:19 +error: First Pass analysis includes: + --> $DIR/multilevel-path-1.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:30:19 | LL | let wp = &w.p; | ^^^ -error: Min Capture w[(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-1.rs:25:19 +error: Min Capture analysis includes: + --> $DIR/multilevel-path-1.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:30:19 | LL | let wp = &w.p; | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs index 020753a71bf3f..540e70138e50e 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] #![allow(unused)] @@ -18,10 +20,13 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("{}", w.p.x); - //~^ ERROR: Capturing w[(0, 0),(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow }; // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr index dfcd2c7a08874..37287f6b3bc74 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/multilevel-path-2.rs:19:13 + --> $DIR/multilevel-path-2.rs:21:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing w[(0, 0),(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-2.rs:22:24 +error: First Pass analysis includes: + --> $DIR/multilevel-path-2.rs:24:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:27:24 | LL | println!("{}", w.p.x); | ^^^^^ -error: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-2.rs:22:24 +error: Min Capture analysis includes: + --> $DIR/multilevel-path-2.rs:24:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:27:24 | LL | println!("{}", w.p.x); | ^^^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index d2e99fe4accf6..88620550f2e7c 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Point { @@ -20,20 +22,26 @@ fn main() { let mut c1 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("{}", p.x); - //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow let incr = 10; let mut c2 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || p.y += incr; - //~^ ERROR: Capturing p[(1, 0)] -> MutBorrow - //~| ERROR: Capturing incr[] -> ImmBorrow - //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow - //~| ERROR: Min Capture incr[] -> ImmBorrow - //~| ERROR: Capturing p[(1, 0)] -> MutBorrow - //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Capturing incr[] -> ImmBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture incr[] -> ImmBorrow + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); }; diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr index 2368a450bc6fb..21147be3f1d08 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:21:18 + --> $DIR/nested-closure.rs:23:18 | LL | let mut c1 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let mut c1 = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:28:22 + --> $DIR/nested-closure.rs:33:22 | LL | let mut c2 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,54 +25,86 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:36:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ - -error: Capturing incr[] -> ImmBorrow - --> $DIR/nested-closure.rs:30:19 +note: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:36:19 | LL | || p.y += incr; | ^^^^ -error: Min Capture p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:36:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ - -error: Min Capture incr[] -> ImmBorrow - --> $DIR/nested-closure.rs:30:19 +note: Min Capture incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:36:19 | LL | || p.y += incr; | ^^^^ -error: Capturing p[(0, 0)] -> ImmBorrow - --> $DIR/nested-closure.rs:24:24 +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | println!("{}", p.y); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:29:24 | LL | println!("{}", p.x); | ^^^ - -error: Capturing p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ -error: Min Capture p[(0, 0)] -> ImmBorrow - --> $DIR/nested-closure.rs:24:24 +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | println!("{}", p.y); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:29:24 | LL | println!("{}", p.x); | ^^^ - -error: Min Capture p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ -error: aborting due to 10 previous errors; 1 warning emitted +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs index 4a42970137fff..16acd2f3206c9 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Point { @@ -22,9 +24,12 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("{}", pent.points[5].x); - //~^ ERROR: Capturing pent[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture pent[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr index 7507e550adeb8..3c8d07ed9ba67 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/path-with-array-access.rs:23:13 + --> $DIR/path-with-array-access.rs:25:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing pent[(0, 0)] -> ImmBorrow - --> $DIR/path-with-array-access.rs:26:24 +error: First Pass analysis includes: + --> $DIR/path-with-array-access.rs:28:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:31:24 | LL | println!("{}", pent.points[5].x); | ^^^^^^^^^^^ -error: Min Capture pent[(0, 0)] -> ImmBorrow - --> $DIR/path-with-array-access.rs:26:24 +error: Min Capture analysis includes: + --> $DIR/path-with-array-access.rs:28:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:31:24 | LL | println!("{}", pent.points[5].x); | ^^^^^^^^^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs index 68c18eac8048e..aaff3531e5850 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] // Test to ensure that min analysis meets capture kind for all paths captured. @@ -24,12 +26,15 @@ fn main() { // let mut c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: p.x += 10; - //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~| ERROR: Min Capture p[] -> MutBorrow + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[] -> MutBorrow println!("{:?}", p); - //~^ ERROR: Capturing p[] -> ImmBorrow + //~^ NOTE: Capturing p[] -> ImmBorrow }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr index 53cc681969ad0..30d3d5f504eb9 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/simple-struct-min-capture.rs:25:17 + --> $DIR/simple-struct-min-capture.rs:27:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,24 +16,47 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(0, 0)] -> MutBorrow - --> $DIR/simple-struct-min-capture.rs:28:9 +error: First Pass analysis includes: + --> $DIR/simple-struct-min-capture.rs:30:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:33:9 | LL | p.x += 10; | ^^^ - -error: Capturing p[] -> ImmBorrow - --> $DIR/simple-struct-min-capture.rs:31:26 +note: Capturing p[] -> ImmBorrow + --> $DIR/simple-struct-min-capture.rs:36:26 | LL | println!("{:?}", p); | ^ -error: Min Capture p[] -> MutBorrow - --> $DIR/simple-struct-min-capture.rs:28:9 +error: Min Capture analysis includes: + --> $DIR/simple-struct-min-capture.rs:30:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:33:9 | LL | p.x += 10; | ^^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs index 889836c11ce88..90b8033d074a1 100644 --- a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] // Test to ensure that we can handle cases where @@ -7,7 +9,9 @@ // using a Place expression // // Note: Currently when feature `capture_disjoint_fields` is enabled -// we can't handle such cases. So the test so the test +// we can't handle such cases. So the test current use `_x` instead of +// `_` until the issue is resolved. +// Check rust-lang/project-rfc-2229#24 for status. struct Point { x: i32, @@ -19,11 +23,14 @@ fn wild_struct() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: // FIXME(arora-aman): Change `_x` to `_` let Point { x: _x, y: _ } = p; - //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow }; c(); @@ -34,11 +41,14 @@ fn wild_tuple() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: // FIXME(arora-aman): Change `_x` to `_` let (_x, _) = t; - //~^ ERROR: Capturing t[(0, 0)] -> ByValue - //~| ERROR: Min Capture t[(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue }; c(); @@ -49,11 +59,14 @@ fn wild_arr() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: // FIXME(arora-aman): Change `_x` to `_` let [_x, _] = arr; - //~^ ERROR: Capturing arr[Index] -> ByValue - //~| ERROR: Min Capture arr[] -> ByValue + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr index 621c8aeb790f9..36be8431be508 100644 --- a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/wild_patterns.rs:20:13 + --> $DIR/wild_patterns.rs:24:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/wild_patterns.rs:35:13 + --> $DIR/wild_patterns.rs:42:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/wild_patterns.rs:50:13 + --> $DIR/wild_patterns.rs:60:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,38 +34,110 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(0, 0)] -> ImmBorrow - --> $DIR/wild_patterns.rs:24:37 +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:31:37 | LL | let Point { x: _x, y: _ } = p; | ^ -error: Min Capture p[(0, 0)] -> ImmBorrow - --> $DIR/wild_patterns.rs:24:37 +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:31:37 | LL | let Point { x: _x, y: _ } = p; | ^ -error: Capturing t[(0, 0)] -> ByValue - --> $DIR/wild_patterns.rs:39:23 +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:45:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:49:23 | LL | let (_x, _) = t; | ^ -error: Min Capture t[(0, 0)] -> ByValue - --> $DIR/wild_patterns.rs:39:23 +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:45:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:49:23 | LL | let (_x, _) = t; | ^ -error: Capturing arr[Index] -> ByValue - --> $DIR/wild_patterns.rs:54:23 +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:63:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/wild_patterns.rs:67:23 | LL | let [_x, _] = arr; | ^^^ -error: Min Capture arr[] -> ByValue - --> $DIR/wild_patterns.rs:54:23 +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:63:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/wild_patterns.rs:67:23 | LL | let [_x, _] = arr; | ^^^ From bb8c5e5d8b4961a26f88b320f719249a9db8225e Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 15 Nov 2020 17:09:51 -0500 Subject: [PATCH 15/16] Fix case when ExprUseVisitor is called after typeck writeback Clippy uses `ExprUseVisitor` and atleast in some cases it runs after writeback. We currently don't writeback the min_capture results of closure capture analysis since no place within the compiler itself uses it. In the short term to fix clippy we add a fallback when walking captures of a closure to check if closure_capture analysis has any entries in it. Writeback for closure_min_captures will be implemented in rust-lang/project-rfc-2229#18 --- compiler/rustc_typeck/src/expr_use_visitor.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 83cc1da69851f..72e5a7ef1b6e1 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -15,6 +15,7 @@ use rustc_index::vec::Idx; use rustc_infer::infer::InferCtxt; use rustc_middle::hir::place::ProjectionKind; use rustc_middle::ty::{self, adjustment, TyCtxt}; +use rustc_span::Span; use rustc_target::abi::VariantIdx; use crate::mem_categorization as mc; @@ -570,6 +571,38 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } + /// Walk closure captures but using `closure_caputes` instead + /// of `closure_min_captures`. + /// + /// This is needed because clippy uses `ExprUseVisitor` after TypeckResults + /// are written back. We don't currently writeback min_captures to + /// TypeckResults. + fn walk_captures_closure_captures(&mut self, closure_expr: &hir::Expr<'_>) { + // FIXME(arora-aman): Remove this function once rust-lang/project-rfc-2229#18 + // is completed. + debug!("walk_captures_closure_captures({:?}), ", closure_expr); + + let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); + let cl_span = self.tcx().hir().span(closure_expr.hir_id); + + let captures = &self.mc.typeck_results.closure_captures[&closure_def_id]; + + for (&var_id, &upvar_id) in captures { + 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, cl_span, var_id)); + match upvar_capture { + ty::UpvarCapture::ByValue(_) => { + let mode = copy_or_move(&self.mc, &captured_place); + self.delegate.consume(&captured_place, captured_place.hir_id, mode); + } + ty::UpvarCapture::ByRef(upvar_borrow) => { + self.delegate.borrow(&captured_place, captured_place.hir_id, upvar_borrow.kind); + } + } + } + } + /// Handle the case where the current body contains a closure. /// /// When the current body being handled is a closure, then we must make sure that @@ -625,6 +658,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { PlaceBase::Upvar(upvar_id), place.projections.clone(), ); + match capture_info.capture_kind { ty::UpvarCapture::ByValue(_) => { let mode = copy_or_move(&self.mc, &place_with_id); @@ -640,8 +674,23 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } } + } else if self.mc.typeck_results.closure_captures.contains_key(&closure_def_id) { + // Handle the case where clippy calls ExprUseVisitor after + self.walk_captures_closure_captures(closure_expr) } } + + fn cat_captured_var( + &mut self, + closure_hir_id: hir::HirId, + closure_span: Span, + var_id: hir::HirId, + ) -> mc::McResult> { + // 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>( From 40dfe1eddd35d65348c40abe12dc5c659d068e2c Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 15 Nov 2020 18:06:30 -0500 Subject: [PATCH 16/16] Ignore doctest for capture analysis examples --- compiler/rustc_middle/src/ty/mod.rs | 6 +++--- compiler/rustc_typeck/src/check/upvar.rs | 13 ++++++++----- compiler/rustc_typeck/src/expr_use_visitor.rs | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0c786be0478de..8f31c9b0fc653 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -797,12 +797,12 @@ pub struct CaptureInfo<'tcx> { /// None. In such case we fallback on uvpars_mentioned for span. /// /// Eg: - /// ```rust - /// let x = ...; + /// ```rust,no_run + /// let x = 5; /// /// let c = || { /// let _ = x - /// } + /// }; /// ``` /// /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index e0bbe3cb07917..019fa78fb1e0e 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -333,7 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// the required captured paths. /// /// Eg: - /// ```rust + /// ```rust,no_run /// struct Point { x: i32, y: i32 } /// /// let s: String; // hir_id_s @@ -575,7 +575,9 @@ struct InferBorrowKind<'a, 'tcx> { /// Consider closure where s.str1 is captured via an ImmutableBorrow and /// s.str2 via a MutableBorrow /// - /// ```rust + /// ```rust,no_run + /// struct SomeStruct { str1: String, str2: String } + /// /// // Assume that the HirId for the variable definition is `V1` /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } /// @@ -584,7 +586,7 @@ struct InferBorrowKind<'a, 'tcx> { /// println!("Updating SomeStruct with str1=", s.str1); /// // Assume that the HirId for the expression `*s.str2` is `E2` /// s.str2 = new_s2; - /// } + /// }; /// ``` /// /// For closure `fix_s`, (at a high level) the map contains @@ -931,7 +933,8 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { /// `determine_capture_info(existing_info, current_info)`. This works out because the /// expressions that occur earlier in the closure body than the current expression are processed before. /// Consider the following example -/// ```rust +/// ```rust,no_run +/// struct Point { x: i32, y: i32 } /// let mut p: Point { x: 10, y: 10 }; /// /// let c = || { @@ -942,7 +945,7 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { /// // ... /// p.x += 10; // E2 /// // ^ E2 ^ -/// } +/// }; /// ``` /// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), /// and both have an expression associated, however for diagnostics we prefer reporting diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 72e5a7ef1b6e1..1b51d5e0182fd 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -611,7 +611,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// In the following example the closures `c` only captures `p.x`` even though `incr` /// is a capture of the nested closure /// - /// ```rust + /// ```rust,ignore(cannot-test-this-because-pseduo-code) /// let p = ..; /// let c = || { /// let incr = 10;