From 16bef5c252e9e6aa0183bf05485873b7ffa53a1d Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Wed, 9 Sep 2020 01:18:28 -0400 Subject: [PATCH 1/6] Use Places to express closure/generator Captures Co-authored-by: Archer Zhang --- Cargo.lock | 1 + 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/Cargo.toml | 1 + compiler/rustc_typeck/src/check/upvar.rs | 327 ++++++++++++------ compiler/rustc_typeck/src/expr_use_visitor.rs | 68 ++-- .../print/generator-print-verbose-2.stderr | 4 +- 8 files changed, 316 insertions(+), 135 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d2170a992747..64b006b600139 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4201,6 +4201,7 @@ dependencies = [ name = "rustc_typeck" version = "0.0.0" dependencies = [ + "indexmap", "rustc_arena", "rustc_ast", "rustc_attr", diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e24ba6d7a1e3d..108bce0e2726f 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 275888b0ce247..99759638d2511 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; @@ -669,6 +670,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. @@ -751,9 +758,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 13e69474cfb96..81b3b7e8d9a47 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -389,8 +389,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/Cargo.toml b/compiler/rustc_typeck/Cargo.toml index e3ba0bea7e8e2..531fe5f70987a 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_typeck/Cargo.toml @@ -9,6 +9,7 @@ test = false doctest = false [dependencies] +indexmap = "1.5.1" rustc_arena = { path = "../rustc_arena" } tracing = "0.1" rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 1e97bd65a79f4..bcfaaddac2d35 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> { @@ -315,31 +394,26 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { // In a case like `let pat = upvar`, don't use the span // of the pattern, as this just looks confusing. - let by_value_span = match tcx.hir().get(place_with_id.hir_id) { - hir::Node::Pat(_) => None, - _ => Some(usage_span), + let (by_value_span, by_value_expr) = match tcx.hir().get(place_with_id.hir_id) { + hir::Node::Pat(_) => (None, None), + _ => (Some(usage_span), Some(place_with_id.hir_id)), }; - let new_capture = ty::UpvarCapture::ByValue(by_value_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: by_value_expr, + capture_kind: ty::UpvarCapture::ByValue(by_value_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 @@ -348,7 +422,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { fn adjust_upvar_borrow_kind_for_mut(&mut self, place_with_id: &PlaceWithHirId<'tcx>) { debug!("adjust_upvar_borrow_kind_for_mut(place_with_id={:?})", place_with_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() { @@ -362,35 +436,26 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { _ => (), } } - self.adjust_upvar_deref( - upvar_id, - self.fcx.tcx.hir().span(place_with_id.hir_id), - borrow_kind, - ); + self.adjust_upvar_deref(place_with_id, borrow_kind); } } fn adjust_upvar_borrow_kind_for_unique(&mut self, place_with_id: &PlaceWithHirId<'tcx>) { debug!("adjust_upvar_borrow_kind_for_unique(place_with_id={:?})", place_with_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(place_with_id.hir_id), - ty::UniqueImmBorrow, - ); + self.adjust_upvar_deref(place_with_id, ty::UniqueImmBorrow); } } fn adjust_upvar_deref( &mut self, - upvar_id: ty::UpvarId, - place_span: Span, + place_with_id: &PlaceWithHirId<'tcx>, borrow_kind: ty::BorrowKind, ) { assert!(match borrow_kind { @@ -407,15 +472,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, 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(place_with_id.hir_id), + var_name(tcx, upvar_id.var_path.hir_id), + ); + } } /// We infer the borrow_kind with which to borrow upvars in a stack closure. @@ -423,29 +489,39 @@ 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>, + 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={:?}, capture_info={:?}, kind={:?})", + place_with_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(place_with_id.hir_id); + if let ty::UpvarCapture::ByRef(borrow_kind) = capture_kind { + borrow_kind.kind = kind; + } + } } // Take LHS: (ty::ImmBorrow, ty::ImmBorrow) @@ -497,17 +573,54 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } } } + + fn init_capture_info_for_place(&mut self, place_with_id: &PlaceWithHirId<'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 tcx = self.fcx.tcx; + let capture_kind = + self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span); + + let expr_id = if !matches!(tcx.hir().get(place_with_id.hir_id), hir::Node::Pat(_)) { + Some(place_with_id.hir_id) + } else { + None + }; + + 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> { fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, mode: euv::ConsumeMode) { debug!("consume(place_with_id={:?},mode={:?})", place_with_id, mode); + + if !self.capture_information.contains_key(&place_with_id.place) { + self.init_capture_info_for_place(place_with_id); + } + self.adjust_upvar_borrow_kind_for_consume(place_with_id, mode); } fn borrow(&mut self, place_with_id: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { debug!("borrow(place_with_id={:?}, bk={:?})", place_with_id, bk); + if !self.capture_information.contains_key(&place_with_id.place) { + self.init_capture_info_for_place(place_with_id); + } + match bk { ty::ImmBorrow => {} ty::UniqueImmBorrow => { @@ -522,6 +635,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>) { debug!("mutate(assignee_place={:?})", assignee_place); + if !self.capture_information.contains_key(&assignee_place.place) { + self.init_capture_info_for_place(assignee_place); + } + self.adjust_upvar_borrow_kind_for_mut(assignee_place); } } @@ -529,3 +646,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 e16f26c330401..f503d2bbeb72c 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -56,6 +56,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>, } @@ -93,6 +94,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ) -> Self { ExprUseVisitor { mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), + body_owner, delegate, } } @@ -505,7 +507,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,); @@ -542,46 +544,52 @@ 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): yell? }; - 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, mode); + let mode = copy_or_move(&self.mc, &place_with_id); + self.delegate.consume(&place_with_id, mode); } ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow(&captured_place, upvar_borrow.kind); + self.delegate.borrow(&place_with_id, upvar_borrow.kind); } } } } } - - 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 08b85c89de8a12a5b0e474aab9782759f7118348 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 11 Oct 2020 00:14:11 -0400 Subject: [PATCH 2/6] 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 7fbd070a609b7..76c05688aeba9 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -598,6 +598,9 @@ declare_features! ( /// Allows `#[instruction_set(_)]` attribute (active, isa_attribute, "1.48.0", Some(74727), 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 // ------------------------------------------------------------------------- @@ -618,6 +621,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::const_trait_bound_opt_out, sym::lazy_normalization_consts, sym::specialization, + 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 527a49b05389a..2aaebc4fc1570 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -545,6 +545,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 28fef65da070a..08545f56eb9f9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -315,6 +315,7 @@ symbols! { call_mut, call_once, caller_location, + capture_disjoint_fields, cdylib, ceilf32, ceilf64, @@ -895,6 +896,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 bcfaaddac2d35..533e8cdd53f6f 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> { @@ -592,10 +613,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { 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); @@ -646,11 +663,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 1209375edd443c9d3bd304fb1a76c879b45a7ad3 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 12 Oct 2020 20:16:02 -0400 Subject: [PATCH 3/6] 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 | 18 ++++ .../capture-disjoint-field-tuple.stderr | 21 ++++ .../capture-disjoint-field-tuple.stdout | 28 ++++++ .../feature-gate-capture_disjoint_fields.rs | 5 +- ...eature-gate-capture_disjoint_fields.stderr | 2 +- ...eature-gate-capture_disjoint_fields.stdout | 19 ++++ .../filter-on-struct-member.rs | 38 +++++++ .../filter-on-struct-member.stderr | 27 +++++ .../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 | 38 +++++++ .../nested-closure.stderr | 30 ++++++ .../nested-closure.stdout | 98 +++++++++++++++++++ .../path-with-array-access.rs | 27 +++++ .../path-with-array-access.stderr | 21 ++++ .../path-with-array-access.stdout | 32 ++++++ .../simple-struct-min-capture.rs | 33 +++++++ .../simple-struct-min-capture.stderr | 21 ++++ .../simple-struct-min-capture.stdout | 45 +++++++++ 31 files changed, 841 insertions(+), 3 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 diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 533e8cdd53f6f..054afb76ccaae 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..50fc37e2960d3 --- /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..f57f388f53d28 --- /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..8d67d58b290ac --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -0,0 +1,18 @@ +#![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] + || { + 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..c53223d000280 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] || { - //~^ ERROR: attributes on expressions are experimental + 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..8c5ef128f16b5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -0,0 +1,38 @@ +#![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( + #[rustc_capture_analysis] + |v| self.filter.allowed(*v), + ); + //~^ ERROR: attributes on expressions are experimental + } +} + +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..5edc132d563f6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -0,0 +1,27 @@ +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 | | #[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..616ff455a4ebf --- /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..40bab632b78b1 --- /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..57e86eb424936 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -0,0 +1,38 @@ +#![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] + || p.y += incr; + //~^ ERROR: attributes on expressions are experimental + 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..05d9cfe6db9f8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -0,0 +1,27 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Node { + id: i32, + neighbours: [Option>; 2], +} + +fn main() { + let mut node0_0 = Box::new(Node { id: 0, neighbours: Default::default() }); + let mut node0 = Box::new(Node { id: 0, neighbours: [Some(node0_0), None] }); + + let mut node1 = Box::new(Node { id: 1, neighbours: [Some(node0), None] }); + + let mut node2 = Box::new(Node { id: 1, neighbours: [Some(node1), None] }); + + let c = #[rustc_capture_analysis] + || { + println!( + "{}", + node2.neighbours[0].unwrap().neighbours[0].unwrap().neighbours[0].unwrap().id + ); + }; + + c(); +} 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..6bb9b6a75b7c9 --- /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: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/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..bc316d513998f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout @@ -0,0 +1,32 @@ +For closure=DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0}): capture information: { + Place { + base_ty: std::boxed::Box, + base: Upvar( + UpvarId(HirId { owner: DefId(0:7 ~ path_with_array_access[317d]::main), local_id: 4 };`node2`;DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Node, + kind: Deref, + }, + Projection { + ty: [std::option::Option>; 2], + kind: Field( + 1, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:7 ~ path_with_array_access[317d]::main), + local_id: 104, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#36r), + ), + }, +} 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..e8172cc056f92 --- /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 to + // + 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), + ), + }, +} From 08f33f2cedf6d1a20cd5c020acfdf34ea0c98969 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sat, 17 Oct 2020 01:49:11 -0400 Subject: [PATCH 4/6] Add helper function for Capture Esclations and expressions Co-authored-by: Dhruv Jauhar --- compiler/rustc_middle/src/ty/mod.rs | 16 ++ compiler/rustc_typeck/src/check/upvar.rs | 180 ++++++++++-------- .../borrowck-closures-slice-patterns.stderr | 4 +- .../borrowck-move-ref-pattern.stderr | 4 +- ...ef-patterns-closure-captures-inside.stderr | 54 +++--- 5 files changed, 148 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 99759638d2511..7e13c58afc521 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -761,6 +761,22 @@ 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 + /// + /// 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 054afb76ccaae..257397ac4c31f 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,75 @@ 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 CaptureInformation is selected based + /// on the expression they point to, + /// - Some(expr) is preferred over None. + /// - Non-Pattern expressions are preferred over pattern expressions, since pattern expressions + /// can be confusing + /// + /// If 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(_)) => { + // Both are either Some or both are either None + // !(span_a.is_some() ^ span_b.is_some()) + 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, None) => return capture_info_a, + (None, Some(_)) => return capture_info_b, + (Some(_), Some(_)) => {} + }; + + // Safe to unwrap here, since both are Some(_) + let expr_kind_a = self.tcx.hir().get(capture_info_a.expr_id.unwrap()); + let expr_kind_b = self.tcx.hir().get(capture_info_b.expr_id.unwrap()); + + // If A is a pattern and B is not a pattern, then choose B else choose A. + if matches!(expr_kind_a, hir::Node::Pat(_)) && !matches!(expr_kind_b, hir::Node::Pat(_)) + { + return capture_info_b; + } else { + return capture_info_a; + } + } + + 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 RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, + // Take LHS: + (ty::UniqueImmBorrow, ty::ImmBorrow) | (ty::MutBorrow, _) => capture_info_a, + (ty::ImmBorrow, ty::ImmBorrow) | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) => { + bug!("Expected unequal capture kinds"); + } + } + } + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -420,28 +476,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { var_name(tcx, upvar_id.var_path.hir_id), ); - // In a case like `let pat = upvar`, don't use the span - // of the pattern, as this just looks confusing. - let (by_value_span, by_value_expr) = match tcx.hir().get(place_with_id.hir_id) { - hir::Node::Pat(_) => (None, None), - _ => (Some(usage_span), Some(place_with_id.hir_id)), - }; - let capture_info = ty::CaptureInfo { - expr_id: by_value_expr, - capture_kind: ty::UpvarCapture::ByValue(by_value_span), + expr_id: Some(place_with_id.hir_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, - }; + 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 @@ -522,42 +565,28 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id: &PlaceWithHirId<'tcx>, 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={:?}, capture_info={:?}, kind={:?})", - place_with_id, capture_info, kind + place_with_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(place_with_id.hir_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 capture 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(place_with_id.hir_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( @@ -608,16 +637,9 @@ 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); - - let expr_id = if !matches!(tcx.hir().get(place_with_id.hir_id), hir::Node::Pat(_)) { - Some(place_with_id.hir_id) - } else { - None - }; - + let expr_id = Some(place_with_id.hir_id); let capture_info = ty::CaptureInfo { expr_id, capture_kind }; self.capture_information.insert(place_with_id.place.clone(), capture_info); diff --git a/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr b/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr index 7f6c764ec2241..f0b1db4df570a 100644 --- a/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr +++ b/src/test/ui/borrowck/borrowck-closures-slice-patterns.stderr @@ -34,7 +34,7 @@ LL | fn arr_by_move(x: [String; 3]) { LL | let f = || { | -- value moved into closure here LL | let [y, z @ ..] = x; - | - variable moved due to use in closure + | ----------- variable moved due to use in closure LL | }; LL | &x; | ^^ value borrowed here after move @@ -75,7 +75,7 @@ LL | fn arr_box_by_move(x: Box<[String; 3]>) { LL | let f = || { | -- value moved into closure here LL | let [y, z @ ..] = *x; - | - variable moved due to use in closure + | ----------- variable moved due to use in closure LL | }; LL | &x; | ^^ value borrowed here after move diff --git a/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr index 285c203f382df..38f20510f100d 100644 --- a/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr +++ b/src/test/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr @@ -194,13 +194,13 @@ LL | let mut tup = (U, U, U); LL | let c1 = || { | -- value moved into closure here LL | let (ref _x0, _x1, _) = tup; - | --- variable moved due to use in closure + | ----------------- variable moved due to use in closure LL | }; LL | let c2 = || { | ^^ value used here after move LL | LL | let (ref mut _x0, _, _x2) = tup; - | --- use occurs due to use in closure + | --------------------- use occurs due to use in closure error: aborting due to 18 previous errors diff --git a/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr b/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr index f19fed0891740..40927204a330d 100644 --- a/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr +++ b/src/test/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr @@ -8,7 +8,7 @@ LL | let mut closure = || { | -- value moved into closure here LL | // Tuples... LL | let (ref mut borrow, mov) = tup0; - | ---- variable moved due to use in closure + | --------------------- variable moved due to use in closure ... LL | drop(&tup0); | ^^^^^ value borrowed here after move @@ -23,7 +23,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let (mov, _, ref mut borrow) = tup1; - | ---- variable moved due to use in closure + | ------------------------ variable moved due to use in closure ... LL | drop(&tup1); | ^^^^^ value borrowed here after move @@ -38,7 +38,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let (ref borrow, mov) = tup2; - | ---- variable moved due to use in closure + | ----------------- variable moved due to use in closure ... LL | drop(&tup2); | ^^^^^ value borrowed here after move @@ -53,7 +53,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let (mov, _, ref borrow) = tup3; - | ---- variable moved due to use in closure + | -------------------- variable moved due to use in closure ... LL | drop(&tup3); | ^^^^^ value borrowed here after move @@ -68,7 +68,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let (ref borrow, mov) = tup4; - | ---- variable moved due to use in closure + | ----------------- variable moved due to use in closure ... LL | drop(&tup4.0); | ^^^^^^^ value borrowed here after move @@ -83,7 +83,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let [mov @ .., ref borrow] = arr0; - | ---- variable moved due to use in closure + | ---------------------- variable moved due to use in closure ... LL | drop(&arr0); | ^^^^^ value borrowed here after move @@ -98,7 +98,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let [_, ref mut borrow @ .., _, mov] = arr1; - | ---- variable moved due to use in closure + | -------------------------------- variable moved due to use in closure ... LL | let [_, mov1, mov2, mov3, _] = &arr1; | ^^^^^ value borrowed here after move @@ -113,7 +113,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let [mov @ .., ref borrow] = arr2; - | ---- variable moved due to use in closure + | ---------------------- variable moved due to use in closure ... LL | drop(&arr2); | ^^^^^ value borrowed here after move @@ -128,7 +128,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | let [_, ref borrow @ .., _, mov] = arr3; - | ---- variable moved due to use in closure + | ---------------------------- variable moved due to use in closure ... LL | let [_, mov1, mov2, mov3, _] = &arr3; | ^^^^^ value borrowed here after move @@ -142,7 +142,7 @@ LL | let mut tup0: Option<(S, S)> = None; LL | let mut closure = || { | -- value moved into closure here LL | m!((ref mut borrow, mov) = tup0); - | ---- variable moved due to use in closure + | --------------------- variable moved due to use in closure ... LL | drop(&tup0); | ^^^^^ value borrowed here after move @@ -157,7 +157,7 @@ LL | let mut closure = || { | -- value moved into closure here LL | m!((ref mut borrow, mov) = tup0); LL | m!((mov, _, ref mut borrow) = tup1); - | ---- variable moved due to use in closure + | ------------------------ variable moved due to use in closure ... LL | drop(&tup1); | ^^^^^ value borrowed here after move @@ -172,7 +172,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!((ref borrow, mov) = tup2); - | ---- variable moved due to use in closure + | ----------------- variable moved due to use in closure ... LL | drop(&tup2); | ^^^^^ value borrowed here after move @@ -187,7 +187,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!((mov, _, ref borrow) = tup3); - | ---- variable moved due to use in closure + | -------------------- variable moved due to use in closure ... LL | drop(&tup3); | ^^^^^ value borrowed here after move @@ -202,7 +202,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!((ref borrow, mov) = tup4); - | ---- variable moved due to use in closure + | ----------------- variable moved due to use in closure ... LL | m!((ref x, _) = &tup4); | ^^^^^ value borrowed here after move @@ -217,7 +217,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([mov @ .., ref borrow] = arr0); - | ---- variable moved due to use in closure + | ---------------------- variable moved due to use in closure ... LL | drop(&arr0); | ^^^^^ value borrowed here after move @@ -232,7 +232,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([_, ref mut borrow @ .., _, mov] = arr1); - | ---- variable moved due to use in closure + | -------------------------------- variable moved due to use in closure ... LL | m!([_, mov1, mov2, mov3, _] = &arr1); | ^^^^^ value borrowed here after move @@ -247,7 +247,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([mov @ .., ref borrow] = arr2); - | ---- variable moved due to use in closure + | ---------------------- variable moved due to use in closure ... LL | drop(&arr2); | ^^^^^ value borrowed here after move @@ -261,7 +261,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([_, ref borrow @ .., _, mov] = arr3); - | ---- variable moved due to use in closure + | ---------------------------- variable moved due to use in closure ... LL | m!([_, mov1, mov2, mov3, _] = &arr3); | ^^^^^ value borrowed here after move @@ -275,7 +275,7 @@ LL | let mut tup0: Option<(S, S)> = None; LL | let mut closure = || { | -- value moved into closure here LL | m!((ref mut borrow, mov) = tup0); - | ---- variable moved due to use in closure + | --------------------- variable moved due to use in closure ... LL | drop(&tup0); | ^^^^^ value borrowed here after move @@ -290,7 +290,7 @@ LL | let mut closure = || { | -- value moved into closure here LL | m!((ref mut borrow, mov) = tup0); LL | m!((mov, _, ref mut borrow) = tup1); - | ---- variable moved due to use in closure + | ------------------------ variable moved due to use in closure ... LL | drop(&tup1); | ^^^^^ value borrowed here after move @@ -305,7 +305,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!((ref borrow, mov) = tup2); - | ---- variable moved due to use in closure + | ----------------- variable moved due to use in closure ... LL | drop(&tup2); | ^^^^^ value borrowed here after move @@ -320,7 +320,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!((mov, _, ref borrow) = tup3); - | ---- variable moved due to use in closure + | -------------------- variable moved due to use in closure ... LL | drop(&tup3); | ^^^^^ value borrowed here after move @@ -335,7 +335,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!((ref borrow, mov) = tup4); - | ---- variable moved due to use in closure + | ----------------- variable moved due to use in closure ... LL | m!((ref x, _) = &tup4); | ^^^^^ value borrowed here after move @@ -350,7 +350,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([mov @ .., ref borrow] = arr0); - | ---- variable moved due to use in closure + | ---------------------- variable moved due to use in closure ... LL | drop(&arr0); | ^^^^^ value borrowed here after move @@ -365,7 +365,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([_, ref mut borrow @ .., _, mov] = arr1); - | ---- variable moved due to use in closure + | -------------------------------- variable moved due to use in closure ... LL | m!([_, mov1, mov2, mov3, _] = &arr1); | ^^^^^ value borrowed here after move @@ -380,7 +380,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([mov @ .., ref borrow] = arr2); - | ---- variable moved due to use in closure + | ---------------------- variable moved due to use in closure ... LL | drop(&arr2); | ^^^^^ value borrowed here after move @@ -394,7 +394,7 @@ LL | let mut closure = || { | -- value moved into closure here ... LL | m!([_, ref borrow @ .., _, mov] = arr3); - | ---- variable moved due to use in closure + | ---------------------------- variable moved due to use in closure ... LL | m!([_, mov1, mov2, mov3, _] = &arr3); | ^^^^^ value borrowed here after move From 604563c8ae16b25bcaa3167024125079621e9dbc Mon Sep 17 00:00:00 2001 From: Roxane Date: Tue, 20 Oct 2020 23:45:04 -0400 Subject: [PATCH 5/6] Reduce verbosity of capture analysis logs Co-authored-by: Jenny Wills --- compiler/rustc_typeck/src/check/upvar.rs | 51 +++++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 257397ac4c31f..26b1b81a4f580 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -47,15 +47,15 @@ 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); + eprint!("For 1 closure={:?}: ", $closure_def_id); + eprintln!($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),*); + eprint!("For 2 closure={:?}: ", $closure_def_id); + //eprintln!($fmt, $($args),*); } }; } @@ -92,6 +92,42 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn log_new_capture_analysis( + &self, + closure_def_id: rustc_hir::def_id::DefId, + capture_information: &FxIndexMap::, ty::CaptureInfo<'tcx>> + ) { + if self.should_log_capture_analysis(closure_def_id) { + for (place, capture_info) in capture_information { + eprintln!("{:?}", self.tcx.hir().span(capture_info.expr_id.unwrap())); + + let variable_name; + match place.base { + PlaceBase::Upvar(upvar_id) => { + variable_name = var_name(self.tcx, upvar_id.var_path.hir_id).to_string(); + }, + _ => variable_name = String::from("unknown") + } + eprint!("Capturing {}[", variable_name); + + for item in &place.projections { + eprint!("{:?}", item.kind); + } + + let capture_type; + match capture_info.capture_kind { + ty::UpvarCapture::ByValue(value) => { + capture_type = format!("{:?}", value); + }, + ty::UpvarCapture::ByRef(borrow) => { + capture_type = format!("{:?}", borrow.kind); + }, + } + eprintln!("] -> {}", capture_type); + } + } + } + /// Analysis starting point. fn analyze_closure( &self, @@ -169,12 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); - log_capture_analysis!( - self, - closure_def_id, - "capture information: {:#?}", - delegate.capture_information - ); + self.log_new_capture_analysis(closure_def_id, &delegate.capture_information); if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure From 42f7d094bf3e1463c94f9003c3bc0f0c157d7c5e Mon Sep 17 00:00:00 2001 From: Roxane Date: Wed, 21 Oct 2020 21:02:27 -0400 Subject: [PATCH 6/6] Cleanup logging --- compiler/rustc_typeck/src/check/upvar.rs | 53 +++++----- .../arrays-completely-captured.stderr | 9 +- .../arrays-completely-captured.stdout | 20 ---- .../capture-disjoint-field-struct.stderr | 9 +- .../capture-disjoint-field-struct.stdout | 28 ------ .../capture-disjoint-field-tuple.stderr | 9 +- .../capture-disjoint-field-tuple.stdout | 28 ------ ...eature-gate-capture_disjoint_fields.stderr | 9 +- ...eature-gate-capture_disjoint_fields.stdout | 20 ---- .../filter-on-struct-member.stderr | 19 +--- .../filter-on-struct-member.stdout | 32 ------ .../multilevel-path-1.stderr | 9 +- .../multilevel-path-1.stdout | 28 ------ .../multilevel-path-2.stderr | 9 +- .../multilevel-path-2.stdout | 35 ------- .../nested-closure.stderr | 28 +++++- .../nested-closure.stdout | 98 ------------------- .../path-with-array-access.stderr | 9 +- .../path-with-array-access.stdout | 32 ------ .../simple-struct-min-capture.stderr | 15 ++- .../simple-struct-min-capture.stdout | 45 --------- 21 files changed, 128 insertions(+), 416 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 diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 26b1b81a4f580..0fe8e677d5a0f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -39,7 +39,7 @@ 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, ProjectionKind, PlaceWithHirId}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::sym; use rustc_span::{Span, Symbol}; @@ -47,15 +47,14 @@ 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) { - eprint!("For 1 closure={:?}: ", $closure_def_id); + eprint!("For closure={:?}: ", $closure_def_id); eprintln!($fmt); } }; ($fcx:expr, $closure_def_id:expr, $fmt:literal, $($args:expr),*) => { if $fcx.should_log_capture_analysis($closure_def_id) { - eprint!("For 2 closure={:?}: ", $closure_def_id); - //eprintln!($fmt, $($args),*); + eprint!("For closure={:?}: ", $closure_def_id); } }; } @@ -99,31 +98,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { if self.should_log_capture_analysis(closure_def_id) { for (place, capture_info) in capture_information { - eprintln!("{:?}", self.tcx.hir().span(capture_info.expr_id.unwrap())); - - let variable_name; - match place.base { - PlaceBase::Upvar(upvar_id) => { - variable_name = var_name(self.tcx, upvar_id.var_path.hir_id).to_string(); - }, - _ => variable_name = String::from("unknown") - } - eprint!("Capturing {}[", variable_name); - - for item in &place.projections { - eprint!("{:?}", item.kind); + let variable_name = match place.base { + PlaceBase::Upvar(upvar_id) => var_name(self.tcx, upvar_id.var_path.hir_id).to_string(), + _ => String::from("unknown"), + }; + + 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_type; - match capture_info.capture_kind { - ty::UpvarCapture::ByValue(value) => { - capture_type = format!("{:?}", value); - }, - ty::UpvarCapture::ByRef(borrow) => { - capture_type = format!("{:?}", borrow.kind); - }, - } - eprintln!("] -> {}", capture_type); + let capture_kind_str = match capture_info.capture_kind { + ty::UpvarCapture::ByValue(value) => format!("{:?}", value), + ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + }; + + self.tcx.sess.span_err(self.tcx.hir().span(capture_info.expr_id.unwrap()), &format!("Capturing {}[{}] -> {}", variable_name, projections_str, capture_kind_str)); } } } 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..e0396b364a83e 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,13 @@ 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 closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:12:9 + | +LL | m[0] += 10; + | ^ + +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/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout deleted file mode 100644 index b4142a4fd8e46..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout +++ /dev/null @@ -1,20 +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), - ), - }, -} 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..e94eec04455e5 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,13 @@ 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 closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:18:24 + | +LL | println!("{}", p.x); + | ^^^ + +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/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout deleted file mode 100644 index ab7bd60e48d1c..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout +++ /dev/null @@ -1,28 +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), - ), - }, -} 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..7c11f571325f2 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 @@ -16,6 +16,13 @@ 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 closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:10:24 + | +LL | println!("{}", t.0); + | ^^^ + +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/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout deleted file mode 100644 index 517d7564c72ce..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout +++ /dev/null @@ -1,28 +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), - ), - }, -} 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..09e8074cebf77 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,13 @@ 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 closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis +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: 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/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 40ac31b4ad90b..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ /dev/null @@ -1,20 +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), - ), - }, -} 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 5edc132d563f6..78b5f30f77795 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,21 +7,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 +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): Using new-style capture analysis +error: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:24:17 | -LL | self.list.retain( - | ^ ------ immutable borrow later used by call - | _________| - | | -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 -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 560b2aa3b57b2..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout +++ /dev/null @@ -1,32 +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), - ), - }, -} 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..66ceb7e152002 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,13 @@ 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 closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:25:19 + | +LL | let wp = &w.p; + | ^^^ + +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/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout deleted file mode 100644 index 525366cb964f0..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout +++ /dev/null @@ -1,28 +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), - ), - }, -} 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..99c347491dde8 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,13 @@ 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 closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:22:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +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/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout deleted file mode 100644 index f89670c8b7f32..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout +++ /dev/null @@ -1,35 +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), - ), - }, -} 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..caa1798bd24eb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -25,6 +25,32 @@ 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 closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): Using new-style capture analysis +error: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:25:12 + | +LL | || p.y += incr; + | ^^^ + +error: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:25:19 + | +LL | || p.y += incr; + | ^^^^ + +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:22:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:25:12 + | +LL | || p.y += incr; + | ^^^ + +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/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout deleted file mode 100644 index 84d87a75bda98..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout +++ /dev/null @@ -1,98 +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: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.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr index 6bb9b6a75b7c9..b38bd3dac319d 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,13 @@ 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 closure=DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis +error: Capturing node2[Deref,(1, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:22:13 + | +LL | node2.neighbours[0].unwrap().neighbours[0].unwrap().neighbours[0].unwrap().id + | ^^^^^^^^^^^^^^^^ + +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/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout deleted file mode 100644 index bc316d513998f..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout +++ /dev/null @@ -1,32 +0,0 @@ -For closure=DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0}): capture information: { - Place { - base_ty: std::boxed::Box, - base: Upvar( - UpvarId(HirId { owner: DefId(0:7 ~ path_with_array_access[317d]::main), local_id: 4 };`node2`;DefId(0:8 ~ path_with_array_access[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Node, - kind: Deref, - }, - Projection { - ty: [std::option::Option>; 2], - kind: Field( - 1, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:7 ~ path_with_array_access[317d]::main), - local_id: 104, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#36r), - ), - }, -} 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..f01062cfc3145 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,19 @@ 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 closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): Using new-style capture analysis +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:29:26 + | +LL | println!("{:?}", 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/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout deleted file mode 100644 index 02129f1acb55c..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout +++ /dev/null @@ -1,45 +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), - ), - }, -}