From 3dd0a7d6eba173e5169b66fd4417025916aa0f3e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 25 Oct 2020 20:41:28 +0900 Subject: [PATCH 01/39] Do not call `unwrap` with `signatures` option enabled --- compiler/rustc_save_analysis/src/sig.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 747e198cd9324..446a0723d8c35 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -21,7 +21,7 @@ // references. // // Signatures do not include visibility info. I'm not sure if this is a feature -// or an ommission (FIXME). +// or an omission (FIXME). // // FIXME where clauses need implementing, defs/refs in generics are mostly missing. @@ -677,7 +677,7 @@ impl<'hir> Sig for hir::Variant<'hir> { let mut text = self.ident.to_string(); match self.data { hir::VariantData::Struct(fields, r) => { - let id = parent_id.unwrap(); + let id = parent_id.ok_or("Missing id for Variant's parent")?; let name_def = SigElement { id: id_from_hir_id(id, scx), start: offset, From 127a6ede1dd9622db20bee435221205b3a61d0ba Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Wed, 9 Sep 2020 01:18:28 -0400 Subject: [PATCH 02/39] Use Places to express closure/generator Captures Co-authored-by: Archer Zhang --- compiler/rustc_middle/src/ty/context.rs | 7 + compiler/rustc_middle/src/ty/mod.rs | 38 +++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 5 +- compiler/rustc_typeck/src/check/upvar.rs | 320 ++++++++++++------ compiler/rustc_typeck/src/expr_use_visitor.rs | 70 ++-- .../print/generator-print-verbose-2.stderr | 4 +- 6 files changed, 314 insertions(+), 130 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1c6937e685c65..e9bc4b9b90dd5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,6 +415,10 @@ pub struct TypeckResults<'tcx> { /// entire variable. pub closure_captures: ty::UpvarListMap, + /// Given the closure ID this map provides the list of + /// `Place`s and how/why are they captured by the closure. + pub closure_capture_information: ty::CaptureInformationMap<'tcx>, + /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). pub generator_interior_types: Vec>, @@ -442,6 +446,7 @@ impl<'tcx> TypeckResults<'tcx> { tainted_by_errors: None, concrete_opaque_types: Default::default(), closure_captures: Default::default(), + closure_capture_information: Default::default(), generator_interior_types: Default::default(), } } @@ -676,6 +681,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors, ref concrete_opaque_types, ref closure_captures, + ref closure_capture_information, ref generator_interior_types, } = *self; @@ -709,6 +715,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors.hash_stable(hcx, hasher); concrete_opaque_types.hash_stable(hcx, hasher); closure_captures.hash_stable(hcx, hasher); + closure_capture_information.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0042b4a3a4279..5f2d3b7818e7f 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -6,6 +6,7 @@ pub use self::IntVarValue::*; pub use self::Variance::*; use crate::hir::exports::ExportMap; +use crate::hir::place::Place as HirPlace; use crate::ich::StableHashingContext; use crate::middle::cstore::CrateStoreDyn; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; @@ -674,6 +675,12 @@ pub struct UpvarId { pub closure_expr_id: LocalDefId, } +impl UpvarId { + pub fn new(var_hir_id: hir::HirId, closure_def_id: LocalDefId) -> UpvarId { + UpvarId { var_path: UpvarPath { hir_id: var_hir_id }, closure_expr_id: closure_def_id } + } +} + #[derive(Clone, PartialEq, Debug, TyEncodable, TyDecodable, Copy, HashStable)] pub enum BorrowKind { /// Data must be immutable and is aliasable. @@ -756,9 +763,40 @@ pub struct UpvarBorrow<'tcx> { pub region: ty::Region<'tcx>, } +#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] +pub struct CaptureInfo<'tcx> { + /// Expr Id pointing to use that resulting in selecting the current capture kind + pub expr_id: Option, + + /// Capture mode that was selected + pub capture_kind: UpvarCapture<'tcx>, +} + pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; +/// Consider closure where s.str1 is captured via an ImmutableBorrow and s.str2 via a MutableBorrow +/// +/// ```rust +/// // Assume that thte HirId for the variable definition is `V1` +/// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } +/// +/// let fix_s = |new_s2| { +/// // Assume that the HirId for the expression `s.str1` is `E1` +/// println!("Updating SomeStruct with str1=", s.str1); +/// // Assume that the HirId for the expression `*s.str2` is `E2` +/// s.str2 = new_s2; +/// } +/// ``` +/// +/// For closure `fix_s`, (at a high level) the IndexMap will contain: +/// +/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } +/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } +/// +pub type CaptureInformationMap<'tcx> = + FxHashMap, CaptureInfo<'tcx>>>; + #[derive(Clone, Copy, PartialEq, Eq)] pub enum IntVarValue { IntType(ast::IntTy), diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 6ed7ed575fcb5..47c0400533bd8 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -387,8 +387,9 @@ fn make_mirror_unadjusted<'a, 'tcx>( } }; let upvars = cx - .tcx - .upvars_mentioned(def_id) + .typeck_results() + .closure_captures + .get(&def_id) .iter() .flat_map(|upvars| upvars.iter()) .zip(substs.upvar_tys()) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index e9dfef718fde9..24bb7756ef36f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -32,6 +32,8 @@ use super::FnCtxt; +use std::env; + use crate::expr_use_visitor as euv; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; @@ -39,10 +41,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; -use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; +use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::{Span, Symbol}; -use std::collections::hash_map::Entry; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { @@ -111,40 +112,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - let mut closure_captures: FxIndexMap = - FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default()); - for (&var_hir_id, _) in upvars.iter() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: closure_def_id.expect_local(), - }; - debug!("seed upvar_id {:?}", upvar_id); - // Adding the upvar Id to the list of Upvars, which will be added - // to the map for the closure at the end of the for loop. - closure_captures.insert(var_hir_id, upvar_id); - - let capture_kind = match capture_clause { - hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None), - hir::CaptureBy::Ref => { - let origin = UpvarRegion(upvar_id, span); - let upvar_region = self.next_region_var(origin); - let upvar_borrow = - ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; - ty::UpvarCapture::ByRef(upvar_borrow) - } - }; + let local_def_id = closure_def_id.expect_local(); - self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, capture_kind); - } - // Add the vector of upvars to the map keyed with the closure id. - // This gives us an easier access to them without having to call - // tcx.upvars again.. - if !closure_captures.is_empty() { - self.typeck_results - .borrow_mut() - .closure_captures - .insert(closure_def_id, closure_captures); + let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); + if !new_capture_analysis() { + debug!("Using old-style capture analysis"); + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + for (&var_hir_id, _) in upvars.iter() { + let place = self.place_for_root_variable(local_def_id, var_hir_id); + + debug!("seed place {:?}", place); + + let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id); + let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); + let info = ty::CaptureInfo { expr_id: None, capture_kind }; + + capture_information.insert(place, info); + } } } @@ -153,9 +137,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut delegate = InferBorrowKind { fcx: self, closure_def_id, + closure_span: span, + capture_clause, current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_origin: None, - adjust_upvar_captures: ty::UpvarCaptureMap::default(), + capture_information, }; euv::ExprUseVisitor::new( &mut delegate, @@ -182,7 +168,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.typeck_results.borrow_mut().upvar_capture_map.extend(delegate.adjust_upvar_captures); + self.set_closure_captures(closure_def_id, &delegate); + + self.typeck_results + .borrow_mut() + .closure_capture_information + .insert(closure_def_id, delegate.capture_information); // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure @@ -226,15 +217,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let closure_def_id = tcx.hir().local_def_id(closure_id); - tcx.upvars_mentioned(closure_def_id) + self.typeck_results + .borrow() + .closure_captures + .get(&closure_def_id.to_def_id()) .iter() .flat_map(|upvars| { upvars.iter().map(|(&var_hir_id, _)| { let upvar_ty = self.node_ty(var_hir_id); - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: closure_def_id, - }; + let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); let capture = self.typeck_results.borrow().upvar_capture(upvar_id); debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture); @@ -250,6 +241,90 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect() } + + fn set_closure_captures( + &self, + closure_def_id: DefId, + inferred_info: &InferBorrowKind<'_, 'tcx>, + ) { + let mut closure_captures: FxIndexMap = Default::default(); + + for (place, capture_info) in inferred_info.capture_information.iter() { + let upvar_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id, + base => bug!("Expected upvar, found={:?}", base), + }; + + assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local()); + + let var_hir_id = upvar_id.var_path.hir_id; + closure_captures.insert(var_hir_id, upvar_id); + + let mut new_capture_kind = capture_info.capture_kind; + if let Some(existing_capture_kind) = + self.typeck_results.borrow_mut().upvar_capture_map.get(&upvar_id) + { + // FIXME(@azhng): refactor this later + new_capture_kind = match (existing_capture_kind, new_capture_kind) { + (ty::UpvarCapture::ByValue(Some(_)), _) => *existing_capture_kind, + (_, ty::UpvarCapture::ByValue(Some(_))) => new_capture_kind, + (ty::UpvarCapture::ByValue(_), _) | (_, ty::UpvarCapture::ByValue(_)) => { + ty::UpvarCapture::ByValue(None) + } + (ty::UpvarCapture::ByRef(existing_ref), ty::UpvarCapture::ByRef(new_ref)) => { + match (existing_ref.kind, new_ref.kind) { + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => new_capture_kind, + // Take LHS: + (ty::ImmBorrow, ty::ImmBorrow) + | (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow) + | (ty::MutBorrow, _) => *existing_capture_kind, + } + } + }; + } + self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind); + } + + if !closure_captures.is_empty() { + self.typeck_results + .borrow_mut() + .closure_captures + .insert(closure_def_id, closure_captures); + } + } + + fn init_capture_kind( + &self, + capture_clause: hir::CaptureBy, + upvar_id: ty::UpvarId, + closure_span: Span, + ) -> ty::UpvarCapture<'tcx> { + match capture_clause { + hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None), + hir::CaptureBy::Ref => { + let origin = UpvarRegion(upvar_id, closure_span); + let upvar_region = self.next_region_var(origin); + let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; + ty::UpvarCapture::ByRef(upvar_borrow) + } + } + } + + fn place_for_root_variable( + &self, + closure_def_id: LocalDefId, + var_hir_id: hir::HirId, + ) -> Place<'tcx> { + let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); + + Place { + base_ty: self.node_ty(var_hir_id), + base: PlaceBase::Upvar(upvar_id), + projections: Default::default(), + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -258,6 +333,10 @@ struct InferBorrowKind<'a, 'tcx> { // The def-id of the closure whose kind and upvar accesses are being inferred. closure_def_id: DefId, + closure_span: Span, + + capture_clause: hir::CaptureBy, + // The kind that we have inferred that the current closure // requires. Note that we *always* infer a minimal kind, even if // we don't always *use* that in the final result (i.e., sometimes @@ -272,7 +351,7 @@ struct InferBorrowKind<'a, 'tcx> { // For each upvar that we access, we track the minimal kind of // access we need (ref, ref mut, move, etc). - adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>, + capture_information: FxIndexMap, ty::CaptureInfo<'tcx>>, } impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { @@ -314,26 +393,21 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { var_name(tcx, upvar_id.var_path.hir_id), ); - let new_capture = ty::UpvarCapture::ByValue(Some(usage_span)); - match self.adjust_upvar_captures.entry(upvar_id) { - Entry::Occupied(mut e) => { - match e.get() { - // We always overwrite `ByRef`, since we require - // that the upvar be available by value. - // - // If we had a previous by-value usage without a specific - // span, use ours instead. Otherwise, keep the first span - // we encountered, since there isn't an obviously better one. - ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => { - e.insert(new_capture); - } - _ => {} - } - } - Entry::Vacant(e) => { - e.insert(new_capture); - } - } + let capture_info = ty::CaptureInfo { + expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), + }; + + let curr_info = self.capture_information.get(&place_with_id.place); + let updated_info = match curr_info { + Some(info) => match info.capture_kind { + ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => capture_info, + _ => *info, + }, + None => capture_info, + }; + + self.capture_information.insert(place_with_id.place.clone(), updated_info); } /// Indicates that `place_with_id` is being directly mutated (e.g., assigned @@ -349,7 +423,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id ); - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + if let PlaceBase::Upvar(_) = place_with_id.place.base { let mut borrow_kind = ty::MutBorrow; for pointer_ty in place_with_id.place.deref_tys() { match pointer_ty.kind() { @@ -363,7 +437,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { _ => (), } } - self.adjust_upvar_deref(upvar_id, self.fcx.tcx.hir().span(diag_expr_id), borrow_kind); + self.adjust_upvar_deref(place_with_id, diag_expr_id, borrow_kind); } } @@ -377,24 +451,20 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id ); - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + if let PlaceBase::Upvar(_) = place_with_id.place.base { if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { // Raw pointers don't inherit mutability. return; } // for a borrowed pointer to be unique, its base must be unique - self.adjust_upvar_deref( - upvar_id, - self.fcx.tcx.hir().span(diag_expr_id), - ty::UniqueImmBorrow, - ); + self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow); } } fn adjust_upvar_deref( &mut self, - upvar_id: ty::UpvarId, - place_span: Span, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, borrow_kind: ty::BorrowKind, ) { assert!(match borrow_kind { @@ -411,15 +481,16 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { // upvar, then we need to modify the // borrow_kind of the upvar to make sure it // is inferred to mutable if necessary - self.adjust_upvar_borrow_kind(upvar_id, borrow_kind); + self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind); - // also need to be in an FnMut closure since this is not an ImmBorrow - self.adjust_closure_kind( - upvar_id.closure_expr_id, - ty::ClosureKind::FnMut, - place_span, - var_name(tcx, upvar_id.var_path.hir_id), - ); + if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + self.adjust_closure_kind( + upvar_id.closure_expr_id, + ty::ClosureKind::FnMut, + tcx.hir().span(diag_expr_id), + var_name(tcx, upvar_id.var_path.hir_id), + ); + } } /// We infer the borrow_kind with which to borrow upvars in a stack closure. @@ -427,29 +498,40 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { /// moving from left to right as needed (but never right to left). /// Here the argument `mutbl` is the borrow_kind that is required by /// some particular use. - fn adjust_upvar_borrow_kind(&mut self, upvar_id: ty::UpvarId, kind: ty::BorrowKind) { - let upvar_capture = self - .adjust_upvar_captures - .get(&upvar_id) - .copied() - .unwrap_or_else(|| self.fcx.typeck_results.borrow().upvar_capture(upvar_id)); + fn adjust_upvar_borrow_kind( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + kind: ty::BorrowKind, + ) { + let capture_info = self + .capture_information + .get(&place_with_id.place) + .unwrap_or_else(|| bug!("Upar capture info missing")); + // We init capture_information for each element + debug!( - "adjust_upvar_borrow_kind(upvar_id={:?}, upvar_capture={:?}, kind={:?})", - upvar_id, upvar_capture, kind + "adjust_upvar_borrow_kind(place={:?}, , diag_expr_id={:?}, capture_info={:?}, kind={:?})", + place_with_id, diag_expr_id, capture_info, kind ); - match upvar_capture { + match capture_info.capture_kind { ty::UpvarCapture::ByValue(_) => { // Upvar is already by-value, the strongest criteria. } - ty::UpvarCapture::ByRef(mut upvar_borrow) => { + ty::UpvarCapture::ByRef(upvar_borrow) => { match (upvar_borrow.kind, kind) { // Take RHS: (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) | (ty::UniqueImmBorrow, ty::MutBorrow) => { - upvar_borrow.kind = kind; - self.adjust_upvar_captures - .insert(upvar_id, ty::UpvarCapture::ByRef(upvar_borrow)); + if let Some(ty::CaptureInfo { expr_id, capture_kind }) = + self.capture_information.get_mut(&place_with_id.place) + { + *expr_id = Some(diag_expr_id); + if let ty::UpvarCapture::ByRef(borrow_kind) = capture_kind { + borrow_kind.kind = kind; + } + } } // Take LHS: (ty::ImmBorrow, ty::ImmBorrow) @@ -501,6 +583,33 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } } } + + fn init_capture_info_for_place( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + ) { + if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { + assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); + + debug!("Capturing new place {:?}", place_with_id); + + let tcx = self.fcx.tcx; + let capture_kind = + self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span); + + let expr_id = Some(diag_expr_id); + let capture_info = ty::CaptureInfo { expr_id, capture_kind }; + + if log_capture_analysis() { + debug!("capture_info: {:?}", capture_info); + } + + self.capture_information.insert(place_with_id.place.clone(), capture_info); + } else { + debug!("Not upvar: {:?}", place_with_id); + } + } } impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { @@ -514,7 +623,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", place_with_id, diag_expr_id, mode ); - self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode); + if !self.capture_information.contains_key(&place_with_id.place) { + self.init_capture_info_for_place(place_with_id, diag_expr_id); + } + + self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode); } fn borrow( @@ -528,6 +641,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, bk ); + if !self.capture_information.contains_key(&place_with_id.place) { + self.init_capture_info_for_place(place_with_id, diag_expr_id); + } + match bk { ty::ImmBorrow => {} ty::UniqueImmBorrow => { @@ -541,6 +658,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id); + + if !self.capture_information.contains_key(&assignee_place.place) { + self.init_capture_info_for_place(assignee_place, diag_expr_id); + } + self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id); } } @@ -548,3 +670,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } + +fn new_capture_analysis() -> bool { + matches!(env::var("SG_NEW"), Ok(_)) +} + +fn log_capture_analysis() -> bool { + matches!(env::var("SG_VERBOSE"), Ok(_)) +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 57bd89b9d3da9..a8cac3e0fc820 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -73,6 +73,7 @@ pub enum MutateMode { // This is the code that actually walks the tree. pub struct ExprUseVisitor<'a, 'tcx> { mc: mc::MemCategorizationContext<'a, 'tcx>, + body_owner: LocalDefId, delegate: &'a mut dyn Delegate<'tcx>, } @@ -110,6 +111,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ) -> Self { ExprUseVisitor { mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), + body_owner, delegate, } } @@ -529,7 +531,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("walk_pat(discr_place={:?}, pat={:?})", discr_place, pat); let tcx = self.tcx(); - let ExprUseVisitor { ref mc, ref mut delegate } = *self; + let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { if let PatKind::Binding(_, canonical_id, ..) = pat.kind { debug!("walk_pat: binding place={:?} pat={:?}", place, pat,); @@ -569,31 +571,49 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, fn_decl_span: Span) { + // FIXME(arora-aman): fix the fn_decl_span + fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, _fn_decl_span: Span) { debug!("walk_captures({:?})", closure_expr); - let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id); - if let Some(upvars) = self.tcx().upvars_mentioned(closure_def_id) { - for &var_id in upvars.keys() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_def_id, + // We are currently walking a closure that is within a given body + // We need to process all the captures for this closure. + let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); + let upvars = self.tcx().upvars_mentioned(self.body_owner); + if let Some(closure_capture_information) = + self.mc.typeck_results.closure_capture_information.get(&closure_def_id) + { + for (place, capture_info) in closure_capture_information.iter() { + let var_hir_id = if let PlaceBase::Upvar(upvar_id) = place.base { + upvar_id.var_path.hir_id + } else { + continue; + // FIXME(arora-aman): throw err? }; - let upvar_capture = self.mc.typeck_results.upvar_capture(upvar_id); - let captured_place = return_if_err!(self.cat_captured_var( - closure_expr.hir_id, - fn_decl_span, - var_id, - )); - match upvar_capture { + + if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) { + // The nested closure might be capturing our local variables + // Since for the current body these aren't captures, we will ignore them. + continue; + } + + // The place is being captured by the enclosing closure + // FIXME(arora-aman) Make sure this is valid to do when called from clippy. + let upvar_id = ty::UpvarId::new(var_hir_id, self.body_owner); + let place_with_id = PlaceWithHirId::new( + capture_info.expr_id.unwrap_or(closure_expr.hir_id), + place.base_ty, + PlaceBase::Upvar(upvar_id), + place.projections.clone(), + ); + match capture_info.capture_kind { ty::UpvarCapture::ByValue(_) => { - let mode = copy_or_move(&self.mc, &captured_place); - self.delegate.consume(&captured_place, captured_place.hir_id, mode); + let mode = copy_or_move(&self.mc, &place_with_id); + self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); } ty::UpvarCapture::ByRef(upvar_borrow) => { self.delegate.borrow( - &captured_place, - captured_place.hir_id, + &place_with_id, + place_with_id.hir_id, upvar_borrow.kind, ); } @@ -601,18 +621,6 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } } - - fn cat_captured_var( - &mut self, - closure_hir_id: hir::HirId, - closure_span: Span, - var_id: hir::HirId, - ) -> mc::McResult> { - // Create the place for the variable being borrowed, from the - // perspective of the creator (parent) of the closure. - let var_ty = self.mc.node_ty(var_id)?; - self.mc.cat_res(closure_hir_id, closure_span, var_ty, Res::Local(var_id)) - } } fn copy_or_move<'a, 'tcx>( diff --git a/src/test/ui/generator/print/generator-print-verbose-2.stderr b/src/test/ui/generator/print/generator-print-verbose-2.stderr index f23949091d912..d590f876b8e77 100644 --- a/src/test/ui/generator/print/generator-print-verbose-2.stderr +++ b/src/test/ui/generator/print/generator-print-verbose-2.stderr @@ -8,8 +8,8 @@ LL | assert_send(|| { | ^^^^^^^^^^^ `Cell` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell` - = note: required because of the requirements on the impl of `Send` for `&'_#3r Cell` - = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell) _#17t]` + = note: required because of the requirements on the impl of `Send` for `&'_#4r Cell` + = note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#4r Cell) _#17t]` error: generator cannot be shared between threads safely --> $DIR/generator-print-verbose-2.rs:12:5 From 88310cc0ebf4144205743dbb3a65223deffcf8e6 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 11 Oct 2020 00:14:11 -0400 Subject: [PATCH 03/39] Indroduce feature flag for RFC-2229 Signed-off-by: Aman Arora --- compiler/rustc_feature/src/active.rs | 4 ++ compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_span/src/symbol.rs | 2 + compiler/rustc_typeck/src/check/upvar.rs | 41 +++++++++++-------- .../feature-gate-capture_disjoint_fields.rs | 12 ++++++ ...eature-gate-capture_disjoint_fields.stderr | 21 ++++++++++ ...eature-gate-capture_disjoint_fields.stdout | 1 + 7 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0df67b63eba58..a035507924794 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -616,6 +616,9 @@ declare_features! ( /// Enables `#[cfg(panic = "...")]` config key. (active, cfg_panic, "1.49.0", Some(77443), None), + /// Allows capturing disjoint fields in a closure/generator (RFC 2229). + (active, capture_disjoint_fields, "1.49.0", Some(53488), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -639,6 +642,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::inline_const, sym::repr128, sym::unsized_locals, + sym::capture_disjoint_fields, ]; /// Some features are not allowed to be used together at the same time, if diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5c5cf609ac33c..fa8edba629e92 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -547,6 +547,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)), + rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)), rustc_attr!(TEST, rustc_variance, Normal, template!(Word)), rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")), rustc_attr!(TEST, rustc_regions, Normal, template!(Word)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ad58f89d87da7..3a2a3adce35c9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -318,6 +318,7 @@ symbols! { call_mut, call_once, caller_location, + capture_disjoint_fields, cdylib, ceilf32, ceilf64, @@ -909,6 +910,7 @@ symbols! { rustc_args_required_const, rustc_attrs, rustc_builtin_macro, + rustc_capture_analysis, rustc_clean, rustc_const_stable, rustc_const_unstable, diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 24bb7756ef36f..a28744c2ece75 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -32,8 +32,6 @@ use super::FnCtxt; -use std::env; - use crate::expr_use_visitor as euv; use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; @@ -43,8 +41,25 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_span::sym; use rustc_span::{Span, Symbol}; +macro_rules! log_capture_analysis { + ($fcx:expr, $closure_def_id:expr, $fmt:literal) => { + if $fcx.should_log_capture_analysis($closure_def_id) { + print!("For closure={:?}: ", $closure_def_id); + println!($fmt); + } + }; + + ($fcx:expr, $closure_def_id:expr, $fmt:literal, $($args:expr),*) => { + if $fcx.should_log_capture_analysis($closure_def_id) { + print!("For closure={:?}: ", $closure_def_id); + println!($fmt, $($args),*); + } + }; +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { InferBorrowKindVisitor { fcx: self }.visit_body(body); @@ -115,8 +130,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); - if !new_capture_analysis() { - debug!("Using old-style capture analysis"); + if self.tcx.features().capture_disjoint_fields { + log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis"); + } else { + log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis"); if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { for (&var_hir_id, _) in upvars.iter() { let place = self.place_for_root_variable(local_def_id, var_hir_id); @@ -325,6 +342,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { projections: Default::default(), } } + + fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool { + self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) + } } struct InferBorrowKind<'a, 'tcx> { @@ -601,10 +622,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { let expr_id = Some(diag_expr_id); let capture_info = ty::CaptureInfo { expr_id, capture_kind }; - if log_capture_analysis() { - debug!("capture_info: {:?}", capture_info); - } - self.capture_information.insert(place_with_id.place.clone(), capture_info); } else { debug!("Not upvar: {:?}", place_with_id); @@ -670,11 +687,3 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } - -fn new_capture_analysis() -> bool { - matches!(env::var("SG_NEW"), Ok(_)) -} - -fn log_capture_analysis() -> bool { - matches!(env::var("SG_VERBOSE"), Ok(_)) -} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs new file mode 100644 index 0000000000000..5eab718736cb2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -0,0 +1,12 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +fn main() { + let s = format!("s"); + + let c = #[rustc_capture_analysis] || { + //~^ ERROR: attributes on expressions are experimental + println!("This uses new capture analyysis to capture s={}", s); + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr new file mode 100644 index 0000000000000..4dc1f9a6ab27b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] || { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/feature-gate-capture_disjoint_fields.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout new file mode 100644 index 0000000000000..c1fca9afd3910 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout @@ -0,0 +1 @@ +For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis From 58e8f8fd2cac4cfcde6c7a1488dd8657dcacaad8 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 12 Oct 2020 20:16:02 -0400 Subject: [PATCH 04/39] Add initial set of testcases for RFC 2229 Co-authored-by: Dhruv Jauhar --- compiler/rustc_typeck/src/check/upvar.rs | 7 ++ .../arrays-completely-captured.rs | 17 ++++ .../arrays-completely-captured.stderr | 21 ++++ .../arrays-completely-captured.stdout | 20 ++++ .../capture-disjoint-field-struct.rs | 26 +++++ .../capture-disjoint-field-struct.stderr | 21 ++++ .../capture-disjoint-field-struct.stdout | 28 ++++++ .../capture-disjoint-field-tuple.rs | 21 ++++ .../capture-disjoint-field-tuple.stderr | 21 ++++ .../capture-disjoint-field-tuple.stdout | 28 ++++++ .../feature-gate-capture_disjoint_fields.rs | 3 +- ...eature-gate-capture_disjoint_fields.stderr | 2 +- ...eature-gate-capture_disjoint_fields.stdout | 19 ++++ .../filter-on-struct-member.rs | 40 ++++++++ .../filter-on-struct-member.stderr | 28 ++++++ .../filter-on-struct-member.stdout | 32 ++++++ .../multilevel-path-1.rs | 34 +++++++ .../multilevel-path-1.stderr | 21 ++++ .../multilevel-path-1.stdout | 28 ++++++ .../multilevel-path-2.rs | 30 ++++++ .../multilevel-path-2.stderr | 21 ++++ .../multilevel-path-2.stdout | 35 +++++++ .../2229_closure_analysis/nested-closure.rs | 40 ++++++++ .../nested-closure.stderr | 30 ++++++ .../nested-closure.stdout | 98 +++++++++++++++++++ .../path-with-array-access.rs | 28 ++++++ .../path-with-array-access.stderr | 21 ++++ .../path-with-array-access.stdout | 28 ++++++ .../simple-struct-min-capture.rs | 33 +++++++ .../simple-struct-min-capture.stderr | 21 ++++ .../simple-struct-min-capture.stdout | 45 +++++++++ .../2229_closure_analysis/slice-pat.rs | 28 ++++++ .../2229_closure_analysis/slice-pat.stderr | 21 ++++ .../2229_closure_analysis/slice-pat.stdout | 20 ++++ 34 files changed, 914 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout create mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stdout diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index a28744c2ece75..9e4e656536152 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -169,6 +169,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); + log_capture_analysis!( + self, + closure_def_id, + "capture information: {:#?}", + delegate.capture_information + ); + if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs new file mode 100644 index 0000000000000..a5dd202bc072b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -0,0 +1,17 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Ensure that capture analysis results in arrays being completely captured. +fn main() { + let mut m = [1, 2, 3, 4, 5]; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + m[0] += 10; + m[1] += 40; + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr new file mode 100644 index 0000000000000..3d3912d0796ee --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/arrays-completely-captured.rs:9:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/arrays-completely-captured.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout new file mode 100644 index 0000000000000..b4142a4fd8e46 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout @@ -0,0 +1,20 @@ +For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): capture information: { + Place { + base_ty: [i32; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), + local_id: 12, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#6r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs new file mode 100644 index 0000000000000..a2be21cddb48c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -0,0 +1,26 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 10 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", p.x); + }; + + // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. + let py = &mut p.y; + + c(); + *py = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr new file mode 100644 index 0000000000000..9233597c360d2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-struct.rs:15:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-struct.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout new file mode 100644 index 0000000000000..ab7bd60e48d1c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), + local_id: 31, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs new file mode 100644 index 0000000000000..e06cde73158ea --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -0,0 +1,21 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +fn main() { + let mut t = (10, 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", t.0); + }; + + // `c` only captures t.0, therefore mutating t.1 is allowed. + let t1 = &mut t.1; + + c(); + *t1 = 20; +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr new file mode 100644 index 0000000000000..f83487ecce555 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-tuple.rs:8:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-disjoint-field-tuple.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout new file mode 100644 index 0000000000000..517d7564c72ce --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): capture information: { + Place { + base_ty: (i32, i32), + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), + local_id: 28, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index 5eab718736cb2..072ed8eeab6f2 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -5,8 +5,9 @@ fn main() { let s = format!("s"); - let c = #[rustc_capture_analysis] || { + let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + || { println!("This uses new capture analyysis to capture s={}", s); }; } diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr index 4dc1f9a6ab27b..133de1d13e85d 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -1,7 +1,7 @@ error[E0658]: attributes on expressions are experimental --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 | -LL | let c = #[rustc_capture_analysis] || { +LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout index c1fca9afd3910..40ac31b4ad90b 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout @@ -1 +1,20 @@ For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): capture information: { + Place { + base_ty: std::string::String, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), + local_id: 52, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#50r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs new file mode 100644 index 0000000000000..aa251c4526cf3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -0,0 +1,40 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Filter { + div: i32, +} +impl Filter { + fn allowed(&self, x: i32) -> bool { + x % self.div == 1 + } +} + +struct Data { + filter: Filter, + list: Vec, +} +impl Data { + fn update(&mut self) { + // The closure passed to filter only captures self.filter, + // therefore mutating self.list is allowed. + self.list.retain( + //~^ cannot borrow `self.list` as mutable because it is also borrowed as immutable + #[rustc_capture_analysis] + |v| self.filter.allowed(*v), + ); + } +} + +fn main() { + let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() }; + + for i in 1..10 { + d.list.push(i); + } + + d.update(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr new file mode 100644 index 0000000000000..3eb4decdeae29 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -0,0 +1,28 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/filter-on-struct-member.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0502]: cannot borrow `self.list` as mutable because it is also borrowed as immutable + --> $DIR/filter-on-struct-member.rs:22:9 + | +LL | self.list.retain( + | ^ ------ immutable borrow later used by call + | _________| + | | +LL | | +LL | | #[rustc_capture_analysis] +LL | | |v| self.filter.allowed(*v), + | | --- ---- first borrow occurs due to use of `self` in closure + | | | + | | immutable borrow occurs here +LL | | ); + | |_________^ mutable borrow occurs here + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout new file mode 100644 index 0000000000000..560b2aa3b57b2 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout @@ -0,0 +1,32 @@ +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): Using new-style capture analysis +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): capture information: { + Place { + base_ty: &mut Data, + base: Upvar( + UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), + ), + projections: [ + Projection { + ty: Data, + kind: Deref, + }, + Projection { + ty: Filter, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), + local_id: 13, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#7r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs new file mode 100644 index 0000000000000..bd8d52d6a3c7e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -0,0 +1,34 @@ +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + // Only paths that appears within the closure that directly start off + // a variable defined outside the closure are captured. + // + // Therefore `w.p` is captured + // Note that `wp.x` doesn't start off a variable defined outside the closure. + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let wp = &w.p; + println!("{}", wp.x); + }; + + // Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr new file mode 100644 index 0000000000000..bd339a68fa0c6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-1.rs:22:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-1.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout new file mode 100644 index 0000000000000..525366cb964f0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_1[317d]::main), + local_id: 20, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#37r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs new file mode 100644 index 0000000000000..a8aca53bc73fd --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -0,0 +1,30 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] +#![allow(unused)] + +struct Point { + x: i32, + y: i32, +} +struct Wrapper { + p: Point, +} + +fn main() { + let mut w = Wrapper { p: Point { x: 10, y: 10 } }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", w.p.x); + }; + + // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. + let py = &mut w.p.y; + c(); + + *py = 20 +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr new file mode 100644 index 0000000000000..772dfd643eaa0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/multilevel-path-2.rs:19:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/multilevel-path-2.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout new file mode 100644 index 0000000000000..f89670c8b7f32 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout @@ -0,0 +1,35 @@ +For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_2[317d]::main), + local_id: 35, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs new file mode 100644 index 0000000000000..64b69af0f0c1c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -0,0 +1,40 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ warning the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Point { + x: i32, + y: i32, +} + +// This testcase ensures that nested closures are handles properly +// - The nested closure is analyzed first. +// - The capture kind of the nested closure is accounted for by the enclosing closure +// - Any captured path by the nested closure that starts off a local variable in the enclosing +// closure is not listed as a capture of the enclosing closure. + +fn main() { + let mut p = Point { x: 5, y: 20 }; + + let mut c1 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", p.x); + let incr = 10; + let mut c2 = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || p.y += incr; + c2(); + println!("{}", p.y); + }; + + c1(); + + let px = &p.x; + + println!("{}", px); + + c1(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr new file mode 100644 index 0000000000000..dbd9e3655a323 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -0,0 +1,30 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:19:18 + | +LL | let mut c1 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:24:22 + | +LL | let mut c2 = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/nested-closure.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout new file mode 100644 index 0000000000000..84d87a75bda98 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout @@ -0,0 +1,98 @@ +For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): Using new-style capture analysis +For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#109r), + ), + }, + Place { + base_ty: i32, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 72, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#110r), + ), + }, +} +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 37, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#114r), + ), + }, + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#115r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs new file mode 100644 index 0000000000000..c967c0b72d409 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -0,0 +1,28 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +struct Point { + x: f32, + y: f32, +} + +struct Pentagon { + points: [Point; 5], +} + +fn main() { + let p1 = Point { x: 10.0, y: 10.0 }; + let p2 = Point { x: 7.5, y: 12.5 }; + let p3 = Point { x: 15.0, y: 15.0 }; + let p4 = Point { x: 12.5, y: 12.5 }; + let p5 = Point { x: 20.0, y: 10.0 }; + + let pent = Pentagon { points: [p1, p2, p3, p4, p5] }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + println!("{}", pent.points[5].x); + }; +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr new file mode 100644 index 0000000000000..84bcc55d99c97 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/path-with-array-access.rs:23:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/path-with-array-access.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout new file mode 100644 index 0000000000000..b843b0494b93c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout @@ -0,0 +1,28 @@ +For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Pentagon, + base: Upvar( + UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: [Point; 5], + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:9 ~ path_with_array_access[317d]::main), + local_id: 83, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#34r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs new file mode 100644 index 0000000000000..27ab9d6b7359e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -0,0 +1,33 @@ +// FIXME(arora-aman) add run-pass once 2229 is implemented + +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure that min analysis meets capture kind for all paths captured. + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let mut p = Point { x: 10, y: 20 }; + + // + // Requirements: + // p.x -> MutBoorrow + // p -> ImmBorrow + // + // Requirements met when p is captured via MutBorrow + // + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + p.x += 10; + println!("{:?}", p); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr new file mode 100644 index 0000000000000..002d2aaab89a5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/simple-struct-min-capture.rs:25:17 + | +LL | let mut c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/simple-struct-min-capture.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout new file mode 100644 index 0000000000000..02129f1acb55c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout @@ -0,0 +1,45 @@ +For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): capture information: { + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 15, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#34r), + ), + }, + Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), + ), + projections: [], + }: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 35, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, +} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs new file mode 100644 index 0000000000000..fc966c4193e99 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs @@ -0,0 +1,28 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure Index projections are handled properly during capture analysis +// +// The array should be moved in entirety, even though only some elements are used. + +fn main() { + let arr : [String; 5] = [ + format!("A"), + format!("B"), + format!("C"), + format!("D"), + format!("E") + ]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let [a, b, .., e] = arr; + assert_eq!(a, "A"); + assert_eq!(b, "B"); + assert_eq!(e, "E"); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr new file mode 100644 index 0000000000000..5c6b505d64fd3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr @@ -0,0 +1,21 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/slice-pat.rs:18:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/slice-pat.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout new file mode 100644 index 0000000000000..ba9b2bca19679 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout @@ -0,0 +1,20 @@ +For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): Using new-style capture analysis +For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information: { + Place { + base_ty: [std::string::String; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: std::string::String, + kind: Index, + }, + ], + }: CaptureInfo { + expr_id: None, + capture_kind: ByValue( + None, + ), + }, +} From 145312075f02bf4303f5b638f4c6187f43900f1c Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sat, 17 Oct 2020 01:49:11 -0400 Subject: [PATCH 05/39] Add helper function for Capture Esclations and expressions Co-authored-by: Dhruv Jauhar --- compiler/rustc_middle/src/ty/mod.rs | 18 ++- compiler/rustc_typeck/src/check/upvar.rs | 148 ++++++++++-------- .../2229_closure_analysis/slice-pat.stdout | 11 +- 3 files changed, 110 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5f2d3b7818e7f..52b49184cf186 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -765,7 +765,23 @@ pub struct UpvarBorrow<'tcx> { #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] pub struct CaptureInfo<'tcx> { - /// Expr Id pointing to use that resulting in selecting the current capture kind + /// Expr Id pointing to use that resulted in selecting the current capture kind + /// + /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is + /// possible that we don't see the use of a particular place resulting in expr_id being + /// None. In such case we fallback on uvpars_mentioned for span. + /// + /// Eg: + /// ```rust + /// let x = ...; + /// + /// let c = || { + /// let _ = x + /// } + /// ``` + /// + /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured, + /// but we won't see it being used during capture analysis, since it's essentially a discard. pub expr_id: Option, /// Capture mode that was selected diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 9e4e656536152..6365797148547 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -284,30 +284,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let var_hir_id = upvar_id.var_path.hir_id; closure_captures.insert(var_hir_id, upvar_id); - let mut new_capture_kind = capture_info.capture_kind; - if let Some(existing_capture_kind) = + let new_capture_kind = if let Some(capture_kind) = self.typeck_results.borrow_mut().upvar_capture_map.get(&upvar_id) { - // FIXME(@azhng): refactor this later - new_capture_kind = match (existing_capture_kind, new_capture_kind) { - (ty::UpvarCapture::ByValue(Some(_)), _) => *existing_capture_kind, - (_, ty::UpvarCapture::ByValue(Some(_))) => new_capture_kind, - (ty::UpvarCapture::ByValue(_), _) | (_, ty::UpvarCapture::ByValue(_)) => { - ty::UpvarCapture::ByValue(None) - } - (ty::UpvarCapture::ByRef(existing_ref), ty::UpvarCapture::ByRef(new_ref)) => { - match (existing_ref.kind, new_ref.kind) { - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => new_capture_kind, - // Take LHS: - (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow) - | (ty::MutBorrow, _) => *existing_capture_kind, - } - } - }; - } + // upvar_capture_map only stores the UpvarCapture (CaptureKind), + // so we create a fake capture info with no expression. + let fake_capture_info = + ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; + self.determine_capture_info(fake_capture_info, capture_info.clone()).capture_kind + } else { + capture_info.capture_kind + }; self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind); } @@ -353,6 +340,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn should_log_capture_analysis(&self, closure_def_id: DefId) -> bool { self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } + + /// Helper function to determine if we need to escalate CaptureKind from + /// CaptureInfo A to B and returns the escalated CaptureInfo. + /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) + /// + /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based + /// on the `CaptureInfo` containing an associated expression id. + /// + /// If both the CaptureKind and Expression are considered to be equivalent, + /// then `CaptureInfo` A is preferred. + fn determine_capture_info( + &self, + capture_info_a: ty::CaptureInfo<'tcx>, + capture_info_b: ty::CaptureInfo<'tcx>, + ) -> ty::CaptureInfo<'tcx> { + // If the capture kind is equivalent then, we don't need to escalate and can compare the + // expressions. + let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => true, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + ref_a.kind == ref_b.kind + } + _ => false, + }; + + if eq_capture_kind { + match (capture_info_a.expr_id, capture_info_b.expr_id) { + (Some(_), _) | (None, None) => capture_info_a, + (None, Some(_)) => capture_info_b, + } + } else { + match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), _) => capture_info_a, + (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + match (ref_a.kind, ref_b.kind) { + // Take LHS: + (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) + | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, + + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, + + (ty::ImmBorrow, ty::ImmBorrow) + | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) + | (ty::MutBorrow, ty::MutBorrow) => { + bug!("Expected unequal capture kinds"); + } + } + } + } + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -426,16 +467,10 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)), }; - let curr_info = self.capture_information.get(&place_with_id.place); - let updated_info = match curr_info { - Some(info) => match info.capture_kind { - ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => capture_info, - _ => *info, - }, - None => capture_info, - }; + let curr_info = self.capture_information[&place_with_id.place]; + let updated_info = self.fcx.determine_capture_info(curr_info, capture_info); - self.capture_information.insert(place_with_id.place.clone(), updated_info); + self.capture_information[&place_with_id.place] = updated_info; } /// Indicates that `place_with_id` is being directly mutated (e.g., assigned @@ -532,42 +567,28 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { diag_expr_id: hir::HirId, kind: ty::BorrowKind, ) { - let capture_info = self - .capture_information - .get(&place_with_id.place) - .unwrap_or_else(|| bug!("Upar capture info missing")); - // We init capture_information for each element + let curr_capture_info = self.capture_information[&place_with_id.place]; debug!( - "adjust_upvar_borrow_kind(place={:?}, , diag_expr_id={:?}, capture_info={:?}, kind={:?})", - place_with_id, diag_expr_id, capture_info, kind + "adjust_upvar_borrow_kind(place={:?}, diag_expr_id={:?}, capture_info={:?}, kind={:?})", + place_with_id, diag_expr_id, curr_capture_info, kind ); - match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => { - // Upvar is already by-value, the strongest criteria. - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - match (upvar_borrow.kind, kind) { - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => { - if let Some(ty::CaptureInfo { expr_id, capture_kind }) = - self.capture_information.get_mut(&place_with_id.place) - { - *expr_id = Some(diag_expr_id); - if let ty::UpvarCapture::ByRef(borrow_kind) = capture_kind { - borrow_kind.kind = kind; - } - } - } - // Take LHS: - (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::ImmBorrow | ty::UniqueImmBorrow) - | (ty::MutBorrow, _) => {} - } - } - } + if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind { + // It's already captured by value, we don't need to do anything here + return; + } else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind { + // Use the same region as the current capture information + // Doesn't matter since only one of the UpvarBorrow will be used. + let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region }; + + let capture_info = ty::CaptureInfo { + expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), + }; + let updated_info = self.fcx.determine_capture_info(curr_capture_info, capture_info); + self.capture_information[&place_with_id.place] = updated_info; + }; } fn adjust_closure_kind( @@ -622,7 +643,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { debug!("Capturing new place {:?}", place_with_id); - let tcx = self.fcx.tcx; let capture_kind = self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span); diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout index ba9b2bca19679..3e352e2c52584 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout @@ -12,9 +12,16 @@ For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information }, ], }: CaptureInfo { - expr_id: None, + expr_id: Some( + HirId { + owner: DefId(0:3 ~ slice_pat[317d]::main), + local_id: 179, + }, + ), capture_kind: ByValue( - None, + Some( + $DIR/slice-pat.rs:21:33: 21:36 (#0), + ), ), }, } From 8f0c0d656d5ca2b17910ec7990691ae5dcd7c1e4 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sat, 26 Sep 2020 17:07:00 -0400 Subject: [PATCH 06/39] Initial work for doing minimum capture analysis for RFC-2229 Co-authored-by: Chris Pardy Co-authored-by: Logan Mosier --- compiler/rustc_middle/src/ty/context.rs | 13 +- compiler/rustc_middle/src/ty/mod.rs | 30 +- .../src/borrow_check/diagnostics/mod.rs | 27 +- compiler/rustc_typeck/src/check/upvar.rs | 318 ++++++++++++++++-- compiler/rustc_typeck/src/expr_use_visitor.rs | 101 +++--- 5 files changed, 374 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e9bc4b9b90dd5..76ca0c51ce1c3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,9 +415,10 @@ pub struct TypeckResults<'tcx> { /// entire variable. pub closure_captures: ty::UpvarListMap, - /// Given the closure ID this map provides the list of - /// `Place`s and how/why are they captured by the closure. - pub closure_capture_information: ty::CaptureInformationMap<'tcx>, + /// Given the closure DefId this map provides a map of + /// root variables to minimum set of `Place`s (and how) that need to be tracked + /// to support all captures of that closure. + pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>, /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). @@ -446,7 +447,7 @@ impl<'tcx> TypeckResults<'tcx> { tainted_by_errors: None, concrete_opaque_types: Default::default(), closure_captures: Default::default(), - closure_capture_information: Default::default(), + closure_min_captures: Default::default(), generator_interior_types: Default::default(), } } @@ -681,7 +682,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors, ref concrete_opaque_types, ref closure_captures, - ref closure_capture_information, + ref closure_min_captures, ref generator_interior_types, } = *self; @@ -715,7 +716,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors.hash_stable(hcx, hasher); concrete_opaque_types.hash_stable(hcx, hasher); closure_captures.hash_stable(hcx, hasher); - closure_capture_information.hash_stable(hcx, hasher); + closure_min_captures.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); }) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 52b49184cf186..c41463d18448d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -788,30 +788,18 @@ pub struct CaptureInfo<'tcx> { pub capture_kind: UpvarCapture<'tcx>, } +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct CapturedPlace<'tcx> { + pub place: HirPlace<'tcx>, + pub info: CaptureInfo<'tcx>, +} + pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; -/// Consider closure where s.str1 is captured via an ImmutableBorrow and s.str2 via a MutableBorrow -/// -/// ```rust -/// // Assume that thte HirId for the variable definition is `V1` -/// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } -/// -/// let fix_s = |new_s2| { -/// // Assume that the HirId for the expression `s.str1` is `E1` -/// println!("Updating SomeStruct with str1=", s.str1); -/// // Assume that the HirId for the expression `*s.str2` is `E2` -/// s.str2 = new_s2; -/// } -/// ``` -/// -/// For closure `fix_s`, (at a high level) the IndexMap will contain: -/// -/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } -/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } -/// -pub type CaptureInformationMap<'tcx> = - FxHashMap, CaptureInfo<'tcx>>>; +pub type MinCaptureList<'tcx> = Vec>; +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; +pub type MinCaptureInformationMap<'tcx> = FxHashMap>; #[derive(Clone, Copy, PartialEq, Eq)] pub enum IntVarValue { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 4256f6e39d5e8..41f3edaa41380 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -383,16 +383,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.describe_field_from_ty(&ty, field, variant_index) } ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => { - // `tcx.upvars_mentioned(def_id)` returns an `Option`, which is `None` in case - // the closure comes from another crate. But in that case we wouldn't - // be borrowck'ing it, so we can just unwrap: - let (&var_id, _) = self - .infcx - .tcx - .upvars_mentioned(def_id) - .unwrap() - .get_index(field.index()) - .unwrap(); + // We won't be borrowck'ing here if the closure came from another crate, + // so it's safe to call `expect_local`. + // + // We know the field exists so it's safe to call operator[] and `unwrap` here. + let (&var_id, _) = + self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] + .get_index(field.index()) + .unwrap(); self.infcx.tcx.hir().name(var_id).to_string() } @@ -967,9 +965,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind; debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr { - for ((upvar_hir_id, upvar), place) in - self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places) + for (upvar_hir_id, place) in + self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id] + .keys() + .zip(places) { + let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span; match place { Operand::Copy(place) | Operand::Move(place) if target_place == place.as_ref() => @@ -991,7 +992,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let usage_span = match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) { ty::UpvarCapture::ByValue(Some(span)) => span, - _ => upvar.span, + _ => span, }; return Some((*args_span, generator_kind, usage_span)); } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 6365797148547..4b6d10357ef11 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -39,11 +39,13 @@ use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; -use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId}; +use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::sym; use rustc_span::{Span, Symbol}; +use std::env; + macro_rules! log_capture_analysis { ($fcx:expr, $closure_def_id:expr, $fmt:literal) => { if $fcx.should_log_capture_analysis($closure_def_id) { @@ -60,6 +62,17 @@ macro_rules! log_capture_analysis { }; } +/// Describe the relationship between the paths of two places +/// eg: +/// - foo is ancestor of foo.bar.baz +/// - foo.bar.baz is an descendant of foo.bar, +/// - foo.bar and foo.baz are divergent +enum PlaceAncestryRelation { + Ancestor, + Descendant, + Divergent, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { InferBorrowKindVisitor { fcx: self }.visit_body(body); @@ -130,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); - if self.tcx.features().capture_disjoint_fields { + if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) { log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis"); } else { log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis"); @@ -192,12 +205,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.set_closure_captures(closure_def_id, &delegate); - - self.typeck_results - .borrow_mut() - .closure_capture_information - .insert(closure_def_id, delegate.capture_information); + self.compute_min_captures(closure_def_id, delegate); + self.set_closure_captures(closure_def_id); // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure @@ -266,43 +275,221 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - fn set_closure_captures( + /// Bridge for closure analysis + /// ---------------------------- + /// + /// For closure with DefId `c`, the bridge converts structures required for supporting RFC 2229, + /// to structures currently used in the compiler for handling closure captures. + /// + /// For example the following structure will be converted: + /// + /// closure_min_captures + /// foo -> [ {foo.x, ImmBorrow}, {foo.y, MutBorrow} ] + /// bar -> [ {bar.z, ByValue}, {bar.q, MutBorrow} ] + /// + /// to + /// + /// 1. closure_captures + /// foo -> UpvarId(foo, c), bar -> UpvarId(bar, c) + /// + /// 2. upvar_capture_map + /// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue + + fn set_closure_captures(&self, closure_def_id: DefId) { + let mut closure_captures: FxIndexMap = Default::default(); + let mut upvar_capture_map = ty::UpvarCaptureMap::default(); + + if let Some(min_captures) = + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) + { + for (var_hir_id, min_list) in min_captures.iter() { + for captured_place in min_list { + let place = &captured_place.place; + let capture_info = captured_place.info; + + let upvar_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id, + base => bug!("Expected upvar, found={:?}", base), + }; + + assert_eq!(upvar_id.var_path.hir_id, *var_hir_id); + assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local()); + + closure_captures.insert(*var_hir_id, upvar_id); + + let new_capture_kind = if let Some(capture_kind) = + upvar_capture_map.get(&upvar_id) + { + // upvar_capture_map only stores the UpvarCapture (CaptureKind), + // so we create a fake capture info with no expression. + let fake_capture_info = + ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; + self.determine_capture_info(fake_capture_info, capture_info.clone()) + .capture_kind + } else { + capture_info.capture_kind + }; + upvar_capture_map.insert(upvar_id, new_capture_kind); + } + } + } + debug!( + "For closure_def_id={:?}, set_closure_captures={:#?}", + closure_def_id, closure_captures + ); + debug!( + "For closure_def_id={:?}, upvar_capture_map={:#?}", + closure_def_id, upvar_capture_map + ); + + if !closure_captures.is_empty() { + self.typeck_results + .borrow_mut() + .closure_captures + .insert(closure_def_id, closure_captures); + + self.typeck_results.borrow_mut().upvar_capture_map.extend(upvar_capture_map); + } + } + + /// Analyses the information collected by InferBorrowKind to compute the min number of + /// Places (and corresponding capture kind) that we need to keep track of to support all + /// the required captured paths. + /// + /// Eg: + /// ```rust + /// struct Point { x: i32, y: i32 } + /// + /// let s: String; // hir_id_s + /// let mut p: Point; // his_id_p + /// let c = || { + /// println!("{}", s); // L1 + /// p.x += 10; // L2 + /// println!("{}" , p.y) // L3 + /// println!("{}", p) // L4 + /// drop(s); // L5 + /// }; + /// ``` + /// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on + /// the lines L1..5 respectively. + /// + /// InferBorrowKind results in a structure like this: + /// + /// ``` + /// { + /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue), + /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow)) + /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow)) + /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow)) + /// ``` + /// + /// After the min capture analysis, we get: + /// ``` + /// { + /// hir_id_s -> [ + /// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue) + /// ], + /// hir_id_p -> [ + /// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)), + /// ], + /// ``` + fn compute_min_captures( &self, closure_def_id: DefId, - inferred_info: &InferBorrowKind<'_, 'tcx>, + inferred_info: InferBorrowKind<'_, 'tcx>, ) { - let mut closure_captures: FxIndexMap = Default::default(); + let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); - for (place, capture_info) in inferred_info.capture_information.iter() { - let upvar_id = match place.base { - PlaceBase::Upvar(upvar_id) => upvar_id, + for (place, capture_info) in inferred_info.capture_information.into_iter() { + let var_hir_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), }; - assert_eq!(upvar_id.closure_expr_id, closure_def_id.expect_local()); - - let var_hir_id = upvar_id.var_path.hir_id; - closure_captures.insert(var_hir_id, upvar_id); - - let new_capture_kind = if let Some(capture_kind) = - self.typeck_results.borrow_mut().upvar_capture_map.get(&upvar_id) - { - // upvar_capture_map only stores the UpvarCapture (CaptureKind), - // so we create a fake capture info with no expression. - let fake_capture_info = - ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; - self.determine_capture_info(fake_capture_info, capture_info.clone()).capture_kind - } else { - capture_info.capture_kind + // Arrays are captured in entirety, drop Index projections and projections + // after Index projections. + let first_index_projection = + place.projections.split(|proj| ProjectionKind::Index == proj.kind).next(); + let place = Place { + base_ty: place.base_ty, + base: place.base, + projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()), + }; + + let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { + None => { + let min_cap_list = vec![ty::CapturedPlace { place: place, info: capture_info }]; + root_var_min_capture_list.insert(var_hir_id, min_cap_list); + continue; + } + Some(min_cap_list) => min_cap_list, }; - self.typeck_results.borrow_mut().upvar_capture_map.insert(upvar_id, new_capture_kind); + + // Go through each entry in the current list of min_captures + // - if ancestor is found, update it's capture kind to account for current place's + // capture information. + // + // - if descendant is found, remove it from the list, and update the current place's + // capture information to account for the descendants's capture kind. + // + // We can never be in a case where the list contains both an ancestor and a descendant + // Also there can only be ancestor but in case of descendants there might be + // multiple. + + let mut descendant_found = false; + let mut updated_capture_info = capture_info; + min_cap_list.retain(|possible_descendant| { + match determine_place_ancestry_relation(&place, &possible_descendant.place) { + // current place is ancestor of possible_descendant + PlaceAncestryRelation::Ancestor => { + descendant_found = true; + updated_capture_info = self + .determine_capture_info(updated_capture_info, possible_descendant.info); + false + } + + _ => true, + } + }); + + let mut ancestor_found = false; + if !descendant_found { + for possible_ancestor in min_cap_list.iter_mut() { + match determine_place_ancestry_relation(&place, &possible_ancestor.place) { + // current place is descendant of possible_ancestor + PlaceAncestryRelation::Descendant => { + ancestor_found = true; + possible_ancestor.info = + self.determine_capture_info(possible_ancestor.info, capture_info); + + // Only one ancestor of the current place will be in the list. + break; + } + _ => {} + } + } + } + + // Only need to insert when we don't have an ancestor in the existing min capture list + if !ancestor_found { + let captured_place = + ty::CapturedPlace { place: place.clone(), info: updated_capture_info }; + min_cap_list.push(captured_place); + } } - if !closure_captures.is_empty() { + log_capture_analysis!( + self, + closure_def_id, + "min_captures={:#?}", + root_var_min_capture_list + ); + + if !root_var_min_capture_list.is_empty() { self.typeck_results .borrow_mut() - .closure_captures - .insert(closure_def_id, closure_captures); + .closure_min_captures + .insert(closure_def_id, root_var_min_capture_list); } } @@ -418,8 +605,27 @@ struct InferBorrowKind<'a, 'tcx> { // variable access that caused us to do so. current_origin: Option<(Span, Symbol)>, - // For each upvar that we access, we track the minimal kind of - // access we need (ref, ref mut, move, etc). + /// For each Place that we access, we track the minimal kind of + /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. + /// Consider closure where s.str1 is captured via an ImmutableBorrow and + /// s.str2 via a MutableBorrow + /// + /// ```rust + /// // Assume that the HirId for the variable definition is `V1` + /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") } + /// + /// let fix_s = |new_s2| { + /// // Assume that the HirId for the expression `s.str1` is `E1` + /// println!("Updating SomeStruct with str1=", s.str1); + /// // Assume that the HirId for the expression `*s.str2` is `E2` + /// s.str2 = new_s2; + /// } + /// ``` + /// + /// For closure `fix_s`, (at a high level) the map contains + /// + /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } + /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } capture_information: FxIndexMap, ty::CaptureInfo<'tcx>>, } @@ -714,3 +920,45 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } + +/// Determines the Ancestry relationship of Place A relative to Place B +/// +/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B +/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B +/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other. +fn determine_place_ancestry_relation( + place_a: &Place<'tcx>, + place_b: &Place<'tcx>, +) -> PlaceAncestryRelation { + // If Place A and Place B, don't start off from the same root variable, they are divergent. + if place_a.base != place_b.base { + return PlaceAncestryRelation::Divergent; + } + + // Assume of length of projections_a = n + let projections_a = &place_a.projections; + + // Assume of length of projections_b = m + let projections_b = &place_b.projections; + + let mut same_initial_projections = true; + + for (proj_a, proj_b) in projections_a.iter().zip(projections_b.iter()) { + if proj_a != proj_b { + same_initial_projections = false; + break; + } + } + + if same_initial_projections { + // First min(n, m) projections are the same + // Select Ancestor/Descendant + if projections_b.len() >= projections_a.len() { + PlaceAncestryRelation::Ancestor + } else { + PlaceAncestryRelation::Descendant + } + } else { + PlaceAncestryRelation::Divergent + } +} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index a8cac3e0fc820..83cc1da69851f 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -18,7 +18,6 @@ use rustc_middle::ty::{self, adjustment, TyCtxt}; use rustc_target::abi::VariantIdx; use crate::mem_categorization as mc; -use rustc_span::Span; /////////////////////////////////////////////////////////////////////////// // The Delegate trait @@ -331,8 +330,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_expr(base); } - hir::ExprKind::Closure(_, _, _, fn_decl_span, _) => { - self.walk_captures(expr, fn_decl_span); + hir::ExprKind::Closure(..) => { + self.walk_captures(expr); } hir::ExprKind::Box(ref base) => { @@ -571,51 +570,73 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } - // FIXME(arora-aman): fix the fn_decl_span - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>, _fn_decl_span: Span) { + /// Handle the case where the current body contains a closure. + /// + /// When the current body being handled is a closure, then we must make sure that + /// - The parent closure only captures Places from the nested closure that are not local to it. + /// + /// In the following example the closures `c` only captures `p.x`` even though `incr` + /// is a capture of the nested closure + /// + /// ```rust + /// let p = ..; + /// let c = || { + /// let incr = 10; + /// let nested = || p.x += incr; + /// } + /// ``` + /// + /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing + /// closure as the DefId. + fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { debug!("walk_captures({:?})", closure_expr); - // We are currently walking a closure that is within a given body - // We need to process all the captures for this closure. let closure_def_id = self.tcx().hir().local_def_id(closure_expr.hir_id).to_def_id(); let upvars = self.tcx().upvars_mentioned(self.body_owner); - if let Some(closure_capture_information) = - self.mc.typeck_results.closure_capture_information.get(&closure_def_id) - { - for (place, capture_info) in closure_capture_information.iter() { - let var_hir_id = if let PlaceBase::Upvar(upvar_id) = place.base { - upvar_id.var_path.hir_id - } else { - continue; - // FIXME(arora-aman): throw err? - }; - if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) { - // The nested closure might be capturing our local variables - // Since for the current body these aren't captures, we will ignore them. + // For purposes of this function, generator and closures are equivalent. + let body_owner_is_closure = match self.tcx().type_of(self.body_owner.to_def_id()).kind() { + ty::Closure(..) | ty::Generator(..) => true, + _ => false, + }; + + if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) + { + for (var_hir_id, min_list) in min_captures.iter() { + if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) { + // The nested closure might be capturing the current (enclosing) closure's local variables. + // We check if the root variable is ever mentioned within the enclosing closure, if not + // then for the current body (if it's a closure) these aren't captures, we will ignore them. continue; } + for captured_place in min_list { + let place = &captured_place.place; + let capture_info = captured_place.info; - // The place is being captured by the enclosing closure - // FIXME(arora-aman) Make sure this is valid to do when called from clippy. - let upvar_id = ty::UpvarId::new(var_hir_id, self.body_owner); - let place_with_id = PlaceWithHirId::new( - capture_info.expr_id.unwrap_or(closure_expr.hir_id), - place.base_ty, - PlaceBase::Upvar(upvar_id), - place.projections.clone(), - ); - match capture_info.capture_kind { - ty::UpvarCapture::ByValue(_) => { - let mode = copy_or_move(&self.mc, &place_with_id); - self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow( - &place_with_id, - place_with_id.hir_id, - upvar_borrow.kind, - ); + let upvar_id = if body_owner_is_closure { + // Mark the place to be captured by the enclosing closure + ty::UpvarId::new(*var_hir_id, self.body_owner) + } else { + ty::UpvarId::new(*var_hir_id, closure_def_id.expect_local()) + }; + let place_with_id = PlaceWithHirId::new( + capture_info.expr_id.unwrap_or(closure_expr.hir_id), + place.base_ty, + PlaceBase::Upvar(upvar_id), + place.projections.clone(), + ); + match capture_info.capture_kind { + ty::UpvarCapture::ByValue(_) => { + let mode = copy_or_move(&self.mc, &place_with_id); + self.delegate.consume(&place_with_id, place_with_id.hir_id, mode); + } + ty::UpvarCapture::ByRef(upvar_borrow) => { + self.delegate.borrow( + &place_with_id, + place_with_id.hir_id, + upvar_borrow.kind, + ); + } } } } From b16815b58d2f584b0a337a3456c1238a96b3f443 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 13 Oct 2020 14:02:38 -0400 Subject: [PATCH 07/39] Update tests with min capture information Co-authored-by: Chris Pardy Co-authored-by: Logan Mosier --- .../arrays-completely-captured.stdout | 27 ++++ .../capture-disjoint-field-struct.stdout | 35 +++++ .../capture-disjoint-field-tuple.stdout | 35 +++++ ...eature-gate-capture_disjoint_fields.stdout | 27 ++++ .../filter-on-struct-member.stdout | 39 ++++++ .../multilevel-path-1.stdout | 35 +++++ .../multilevel-path-2.stdout | 42 ++++++ .../nested-closure.stdout | 123 ++++++++++++++++++ .../path-with-array-access.stdout | 35 +++++ .../simple-struct-min-capture.stdout | 27 ++++ .../2229_closure_analysis/slice-pat.stdout | 29 +++++ 11 files changed, 454 insertions(+) diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout index b4142a4fd8e46..e0cd06f765e07 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout @@ -18,3 +18,30 @@ For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): ca ), }, } +For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: [i32; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), + local_id: 12, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#6r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout index ab7bd60e48d1c..d378b12a23500 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): ), }, } +For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), + local_id: 31, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout index 517d7564c72ce..9a080c28d4ebf 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): ), }, } +For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: (i32, i32), + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), + local_id: 28, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout index 40ac31b4ad90b..69722f9d21ec2 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout @@ -18,3 +18,30 @@ For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closu ), }, } +For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: std::string::String, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), + local_id: 52, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#50r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout index 560b2aa3b57b2..44fb88e1520c4 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout @@ -30,3 +30,42 @@ For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closu ), }, } +For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): min_captures={ + HirId { + owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: &mut Data, + base: Upvar( + UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), + ), + projections: [ + Projection { + ty: Data, + kind: Deref, + }, + Projection { + ty: Filter, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), + local_id: 13, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#7r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout index 525366cb964f0..63a3669d0a76d 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): capture inf ), }, } +For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:8 ~ multilevel_path_1[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_1[317d]::main), + local_id: 20, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#37r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout index f89670c8b7f32..576d2c36b6f25 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout @@ -33,3 +33,45 @@ For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): capture inf ), }, } +For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:8 ~ multilevel_path_2[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Wrapper, + base: Upvar( + UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: Point, + kind: Field( + 0, + 0, + ), + }, + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:8 ~ multilevel_path_2[317d]::main), + local_id: 35, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#35r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout index 84d87a75bda98..446419c75f5eb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout @@ -43,6 +43,66 @@ For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): c ), }, } +For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): min_captures={ + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#109r), + ), + }, + }, + ], + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 5, + }: [ + CapturedPlace { + place: Place { + base_ty: i32, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 72, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#110r), + ), + }, + }, + ], +} For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture information: { Place { @@ -96,3 +156,66 @@ For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture inform ), }, } +For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 37, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#114r), + ), + }, + }, + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: i32, + kind: Field( + 1, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:6 ~ nested_closure[317d]::main), + local_id: 70, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#115r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout index b843b0494b93c..73f880c155afd 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout @@ -26,3 +26,38 @@ For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): captu ), }, } +For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:9 ~ path_with_array_access[317d]::main), + local_id: 6, + }: [ + CapturedPlace { + place: Place { + base_ty: Pentagon, + base: Upvar( + UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), + ), + projections: [ + Projection { + ty: [Point; 5], + kind: Field( + 0, + 0, + ), + }, + ], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:9 ~ path_with_array_access[317d]::main), + local_id: 83, + }, + ), + capture_kind: ByRef( + UpvarBorrow(ImmBorrow, '_#34r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout index 02129f1acb55c..c0421852d93d8 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout @@ -43,3 +43,30 @@ For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): cap ), }, } +For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: Point, + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), + local_id: 15, + }, + ), + capture_kind: ByRef( + UpvarBorrow(MutBorrow, '_#34r), + ), + }, + }, + ], +} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout index 3e352e2c52584..43c43015134ae 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout @@ -25,3 +25,32 @@ For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information ), }, } +For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): min_captures={ + HirId { + owner: DefId(0:3 ~ slice_pat[317d]::main), + local_id: 1, + }: [ + CapturedPlace { + place: Place { + base_ty: [std::string::String; 5], + base: Upvar( + UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), + ), + projections: [], + }, + info: CaptureInfo { + expr_id: Some( + HirId { + owner: DefId(0:3 ~ slice_pat[317d]::main), + local_id: 179, + }, + ), + capture_kind: ByValue( + Some( + $DIR/slice-pat.rs:21:33: 21:36 (#0), + ), + ), + }, + }, + ], +} From 825e9e45d14694d23fde29fdab2b02d9973a4eb3 Mon Sep 17 00:00:00 2001 From: Roxane Fruytier Date: Tue, 27 Oct 2020 23:41:52 -0400 Subject: [PATCH 08/39] Reduce verbosity of capture analysis logs Co-authored-by: Jenny Wills Co-authored-by: Aman Arora --- compiler/rustc_typeck/src/check/upvar.rs | 112 ++++++--- .../arrays-completely-captured.rs | 2 + .../arrays-completely-captured.stderr | 14 +- .../arrays-completely-captured.stdout | 47 ---- .../capture-disjoint-field-struct.rs | 2 + .../capture-disjoint-field-struct.stderr | 14 +- .../capture-disjoint-field-struct.stdout | 63 ----- .../capture-disjoint-field-tuple.rs | 2 + .../capture-disjoint-field-tuple.stderr | 18 +- .../capture-disjoint-field-tuple.stdout | 63 ----- .../feature-gate-capture_disjoint_fields.rs | 2 + ...eature-gate-capture_disjoint_fields.stderr | 14 +- ...eature-gate-capture_disjoint_fields.stdout | 47 ---- .../filter-on-struct-member.rs | 3 +- .../filter-on-struct-member.stderr | 29 +-- .../filter-on-struct-member.stdout | 71 ------ .../multilevel-path-1.rs | 2 + .../multilevel-path-1.stderr | 14 +- .../multilevel-path-1.stdout | 63 ----- .../multilevel-path-2.rs | 2 + .../multilevel-path-2.stderr | 14 +- .../multilevel-path-2.stdout | 77 ------ .../2229_closure_analysis/nested-closure.rs | 8 + .../nested-closure.stderr | 56 ++++- .../nested-closure.stdout | 221 ------------------ .../path-with-array-access.rs | 2 + .../path-with-array-access.stderr | 14 +- .../path-with-array-access.stdout | 63 ----- .../simple-struct-min-capture.rs | 3 + .../simple-struct-min-capture.stderr | 20 +- .../simple-struct-min-capture.stdout | 72 ------ .../2229_closure_analysis/slice-pat.rs | 2 + .../2229_closure_analysis/slice-pat.stderr | 14 +- .../2229_closure_analysis/slice-pat.stdout | 56 ----- 34 files changed, 297 insertions(+), 909 deletions(-) delete mode 100644 src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/nested-closure.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout delete mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stdout diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 4b6d10357ef11..a01aa8531ca9c 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -46,22 +46,6 @@ use rustc_span::{Span, Symbol}; use std::env; -macro_rules! log_capture_analysis { - ($fcx:expr, $closure_def_id:expr, $fmt:literal) => { - if $fcx.should_log_capture_analysis($closure_def_id) { - print!("For closure={:?}: ", $closure_def_id); - println!($fmt); - } - }; - - ($fcx:expr, $closure_def_id:expr, $fmt:literal, $($args:expr),*) => { - if $fcx.should_log_capture_analysis($closure_def_id) { - print!("For closure={:?}: ", $closure_def_id); - println!($fmt, $($args),*); - } - }; -} - /// Describe the relationship between the paths of two places /// eg: /// - foo is ancestor of foo.bar.baz @@ -144,9 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) { - log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis"); } else { - log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis"); if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { for (&var_hir_id, _) in upvars.iter() { let place = self.place_for_root_variable(local_def_id, var_hir_id); @@ -182,12 +164,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); - log_capture_analysis!( - self, - closure_def_id, - "capture information: {:#?}", - delegate.capture_information + debug!( + "For closure={:?}, capture_information={:#?}", + closure_def_id, delegate.capture_information ); + self.log_closure_capture_info(closure_def_id, &delegate.capture_information, span); if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure @@ -206,6 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.compute_min_captures(closure_def_id, delegate); + self.log_closure_min_capture_info(closure_def_id, span); + self.set_closure_captures(closure_def_id); // Now that we've analyzed the closure, we know how each @@ -333,10 +316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - debug!( - "For closure_def_id={:?}, set_closure_captures={:#?}", - closure_def_id, closure_captures - ); + debug!("For closure_def_id={:?}, closure_captures={:#?}", closure_def_id, closure_captures); debug!( "For closure_def_id={:?}, upvar_capture_map={:#?}", closure_def_id, upvar_capture_map @@ -478,12 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - log_capture_analysis!( - self, - closure_def_id, - "min_captures={:#?}", - root_var_min_capture_list - ); + debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); if !root_var_min_capture_list.is_empty() { self.typeck_results @@ -581,6 +556,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + + fn log_closure_capture_info( + &self, + closure_def_id: rustc_hir::def_id::DefId, + capture_information: &FxIndexMap, ty::CaptureInfo<'tcx>>, + closure_span: Span, + ) { + if self.should_log_capture_analysis(closure_def_id) { + for (place, capture_info) in capture_information { + let capture_str = construct_capture_info_string(self.tcx, place, capture_info); + let output_str = format!("Capturing {}", capture_str); + + let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + self.tcx.sess.span_err(span, &output_str); + } + } + } + + fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span) { + if self.should_log_capture_analysis(closure_def_id) { + if let Some(min_captures) = + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) + { + for (_, min_captures_for_var) in min_captures { + for capture in min_captures_for_var { + let place = &capture.place; + let capture_info = &capture.info; + + let capture_str = + construct_capture_info_string(self.tcx, place, capture_info); + let output_str = format!("Min Capture {}", capture_str); + + let span = + capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); + self.tcx.sess.span_err(span, &output_str); + } + } + } + } + } } struct InferBorrowKind<'a, 'tcx> { @@ -917,6 +932,37 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } } +fn construct_capture_info_string( + tcx: TyCtxt<'_>, + place: &Place<'tcx>, + capture_info: &ty::CaptureInfo<'tcx>, +) -> String { + let variable_name = match place.base { + PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), + _ => bug!("Capture_information should only contain upvars"), + }; + + let mut projections_str = String::new(); + for (i, item) in place.projections.iter().enumerate() { + let proj = match item.kind { + ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b), + ProjectionKind::Deref => String::from("Deref"), + ProjectionKind::Index => String::from("Index"), + ProjectionKind::Subslice => String::from("Subslice"), + }; + if i != 0 { + projections_str.push_str(","); + } + projections_str.push_str(proj.as_str()); + } + + let capture_kind_str = match capture_info.capture_kind { + ty::UpvarCapture::ByValue(_) => "ByValue".into(), + ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), + }; + format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str) +} + fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index a5dd202bc072b..e144cce54ec22 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -10,6 +10,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { m[0] += 10; + //~^ ERROR: Capturing m[] -> MutBorrow + //~^^ ERROR: Min Capture m[] -> MutBorrow m[1] += 40; }; diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr index 3d3912d0796ee..228682caad78c 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:12:9 + | +LL | m[0] += 10; + | ^ + +error: Min Capture m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:12:9 + | +LL | m[0] += 10; + | ^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout deleted file mode 100644 index e0cd06f765e07..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stdout +++ /dev/null @@ -1,47 +0,0 @@ -For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): capture information: { - Place { - base_ty: [i32; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), - local_id: 12, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#6r), - ), - }, -} -For closure=DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: [i32; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), local_id: 1 };`m`;DefId(0:4 ~ arrays_completely_captured[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ arrays_completely_captured[317d]::main), - local_id: 12, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#6r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index a2be21cddb48c..3f2e16725dc38 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -16,6 +16,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", p.x); + //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow }; // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr index 9233597c360d2..41e641f356418 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:18:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:18:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout deleted file mode 100644 index d378b12a23500..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), - local_id: 31, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ capture_disjoint_field_struct[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ capture_disjoint_field_struct[317d]::main), - local_id: 31, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index e06cde73158ea..5db1c9c0683ec 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -11,6 +11,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", t.0); + //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture t[(0, 0)] -> ImmBorrow }; // `c` only captures t.0, therefore mutating t.1 is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr index f83487ecce555..47470bb9646ea 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-disjoint-field-tuple.rs:8:13 + --> $DIR/capture-disjoint-field-tuple.rs:10:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/capture-disjoint-field-tuple.rs:1:12 + --> $DIR/capture-disjoint-field-tuple.rs:3:12 | LL | #![feature(capture_disjoint_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:13:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:13:24 + | +LL | println!("{}", t.0); + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout deleted file mode 100644 index 9a080c28d4ebf..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): capture information: { - Place { - base_ty: (i32, i32), - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), - local_id: 28, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: (i32, i32), - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), local_id: 1 };`t`;DefId(0:4 ~ capture_disjoint_field_tuple[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ capture_disjoint_field_tuple[317d]::main), - local_id: 28, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index 072ed8eeab6f2..3713abe52df20 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -9,5 +9,7 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("This uses new capture analyysis to capture s={}", s); + //~^ ERROR: Capturing s[] -> ImmBorrow + //~^^ ERROR: Min Capture s[] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr index 133de1d13e85d..6d4d7ee5fbeb4 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: Min Capture s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 + | +LL | println!("This uses new capture analyysis to capture s={}", s); + | ^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout deleted file mode 100644 index 69722f9d21ec2..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stdout +++ /dev/null @@ -1,47 +0,0 @@ -For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): capture information: { - Place { - base_ty: std::string::String, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), - local_id: 52, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#50r), - ), - }, -} -For closure=DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: std::string::String, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), local_id: 1 };`s`;DefId(0:4 ~ feature_gate_capture_disjoint_fields[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ feature_gate_capture_disjoint_fields[317d]::main), - local_id: 52, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#50r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs index aa251c4526cf3..3ef8b4acf9782 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -22,9 +22,10 @@ impl Data { // The closure passed to filter only captures self.filter, // therefore mutating self.list is allowed. self.list.retain( - //~^ cannot borrow `self.list` as mutable because it is also borrowed as immutable #[rustc_capture_analysis] |v| self.filter.allowed(*v), + //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow ); } } diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr index 3eb4decdeae29..452ba5c654516 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -1,5 +1,5 @@ warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/filter-on-struct-member.rs:1:12 + --> $DIR/filter-on-struct-member.rs:3:12 | LL | #![feature(capture_disjoint_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,22 +7,17 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error[E0502]: cannot borrow `self.list` as mutable because it is also borrowed as immutable - --> $DIR/filter-on-struct-member.rs:22:9 +error: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:26:17 | -LL | self.list.retain( - | ^ ------ immutable borrow later used by call - | _________| - | | -LL | | -LL | | #[rustc_capture_analysis] -LL | | |v| self.filter.allowed(*v), - | | --- ---- first borrow occurs due to use of `self` in closure - | | | - | | immutable borrow occurs here -LL | | ); - | |_________^ mutable borrow occurs here +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ -error: aborting due to previous error; 1 warning emitted +error: Min Capture self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:26:17 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout deleted file mode 100644 index 44fb88e1520c4..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stdout +++ /dev/null @@ -1,71 +0,0 @@ -For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): Using new-style capture analysis -For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): capture information: { - Place { - base_ty: &mut Data, - base: Upvar( - UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), - ), - projections: [ - Projection { - ty: Data, - kind: Deref, - }, - Projection { - ty: Filter, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), - local_id: 13, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#7r), - ), - }, -} -For closure=DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0}): min_captures={ - HirId { - owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: &mut Data, - base: Upvar( - UpvarId(HirId { owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), local_id: 1 };`self`;DefId(0:12 ~ filter_on_struct_member[317d]::{impl#1}::update::{closure#0})), - ), - projections: [ - Projection { - ty: Data, - kind: Deref, - }, - Projection { - ty: Filter, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:11 ~ filter_on_struct_member[317d]::{impl#1}::update), - local_id: 13, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#7r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs index bd8d52d6a3c7e..a6793a43488e8 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -23,6 +23,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { let wp = &w.p; + //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture w[(0, 0)] -> ImmBorrow println!("{}", wp.x); }; diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr index bd339a68fa0c6..e8368201ede91 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:25:19 + | +LL | let wp = &w.p; + | ^^^ + +error: Min Capture w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:25:19 + | +LL | let wp = &w.p; + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout deleted file mode 100644 index 63a3669d0a76d..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_1[317d]::main), - local_id: 20, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#37r), - ), - }, -} -For closure=DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:8 ~ multilevel_path_1[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_1[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_1[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_1[317d]::main), - local_id: 20, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#37r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs index a8aca53bc73fd..994e2530f7825 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -20,6 +20,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", w.p.x); + //~^ ERROR: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow }; // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr index 772dfd643eaa0..dfcd2c7a08874 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:22:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:22:24 + | +LL | println!("{}", w.p.x); + | ^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout deleted file mode 100644 index 576d2c36b6f25..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stdout +++ /dev/null @@ -1,77 +0,0 @@ -For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_2[317d]::main), - local_id: 35, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:8 ~ multilevel_path_2[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Wrapper, - base: Upvar( - UpvarId(HirId { owner: DefId(0:8 ~ multilevel_path_2[317d]::main), local_id: 1 };`w`;DefId(0:9 ~ multilevel_path_2[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: Point, - kind: Field( - 0, - 0, - ), - }, - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:8 ~ multilevel_path_2[317d]::main), - local_id: 35, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index 64b69af0f0c1c..8641d6b4f59fb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -22,10 +22,18 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", p.x); + //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow let incr = 10; let mut c2 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental || p.y += incr; + //~^ ERROR: Capturing p[(1, 0)] -> MutBorrow + //~^^ ERROR: Capturing incr[] -> ImmBorrow + //~^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~^^^^ ERROR: Min Capture incr[] -> ImmBorrow + //~^^^^^ ERROR: Capturing p[(1, 0)] -> MutBorrow + //~^^^^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); }; diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr index dbd9e3655a323..2368a450bc6fb 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:19:18 + --> $DIR/nested-closure.rs:21:18 | LL | let mut c1 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let mut c1 = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:24:22 + --> $DIR/nested-closure.rs:28:22 | LL | let mut c2 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let mut c2 = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/nested-closure.rs:1:12 + --> $DIR/nested-closure.rs:3:12 | LL | #![feature(capture_disjoint_fields)] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,6 +25,54 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to 2 previous errors; 1 warning emitted +error: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:30:19 + | +LL | || p.y += incr; + | ^^^^ + +error: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: Min Capture incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:30:19 + | +LL | || p.y += incr; + | ^^^^ + +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:24:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:24:24 + | +LL | println!("{}", p.x); + | ^^^ + +error: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:30:12 + | +LL | || p.y += incr; + | ^^^ + +error: aborting due to 10 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout b/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout deleted file mode 100644 index 446419c75f5eb..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stdout +++ /dev/null @@ -1,221 +0,0 @@ -For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): Using new-style capture analysis -For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#109r), - ), - }, - Place { - base_ty: i32, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 72, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#110r), - ), - }, -} -For closure=DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0}): min_captures={ - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#109r), - ), - }, - }, - ], - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 5, - }: [ - CapturedPlace { - place: Place { - base_ty: i32, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 5 };`incr`;DefId(0:8 ~ nested_closure[317d]::main::{closure#0}::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 72, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#110r), - ), - }, - }, - ], -} -For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 37, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#114r), - ), - }, - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#115r), - ), - }, -} -For closure=DefId(0:7 ~ nested_closure[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 37, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#114r), - ), - }, - }, - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:6 ~ nested_closure[317d]::main), local_id: 1 };`p`;DefId(0:7 ~ nested_closure[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 1, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:6 ~ nested_closure[317d]::main), - local_id: 70, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#115r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs index c967c0b72d409..48e0698b4a750 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -24,5 +24,7 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { println!("{}", pent.points[5].x); + //~^ ERROR: Capturing pent[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture pent[(0, 0)] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr index 84bcc55d99c97..7507e550adeb8 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:26:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: Min Capture pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:26:24 + | +LL | println!("{}", pent.points[5].x); + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout deleted file mode 100644 index 73f880c155afd..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stdout +++ /dev/null @@ -1,63 +0,0 @@ -For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Pentagon, - base: Upvar( - UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: [Point; 5], - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:9 ~ path_with_array_access[317d]::main), - local_id: 83, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#34r), - ), - }, -} -For closure=DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:9 ~ path_with_array_access[317d]::main), - local_id: 6, - }: [ - CapturedPlace { - place: Place { - base_ty: Pentagon, - base: Upvar( - UpvarId(HirId { owner: DefId(0:9 ~ path_with_array_access[317d]::main), local_id: 6 };`pent`;DefId(0:10 ~ path_with_array_access[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: [Point; 5], - kind: Field( - 0, - 0, - ), - }, - ], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:9 ~ path_with_array_access[317d]::main), - local_id: 83, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#34r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs index 27ab9d6b7359e..3d5a08344b72a 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -26,7 +26,10 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { p.x += 10; + //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow + //~^^ ERROR: Min Capture p[] -> MutBorrow println!("{:?}", p); + //~^ ERROR: Capturing p[] -> ImmBorrow }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr index 002d2aaab89a5..53cc681969ad0 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -16,6 +16,24 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:28:9 + | +LL | p.x += 10; + | ^^^ + +error: Capturing p[] -> ImmBorrow + --> $DIR/simple-struct-min-capture.rs:31:26 + | +LL | println!("{:?}", p); + | ^ + +error: Min Capture p[] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:28:9 + | +LL | p.x += 10; + | ^^^ + +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout deleted file mode 100644 index c0421852d93d8..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stdout +++ /dev/null @@ -1,72 +0,0 @@ -For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): capture information: { - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: i32, - kind: Field( - 0, - 0, - ), - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 15, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#34r), - ), - }, - Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), - ), - projections: [], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 35, - }, - ), - capture_kind: ByRef( - UpvarBorrow(ImmBorrow, '_#35r), - ), - }, -} -For closure=DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: Point, - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), local_id: 1 };`p`;DefId(0:4 ~ simple_struct_min_capture[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ simple_struct_min_capture[317d]::main), - local_id: 15, - }, - ), - capture_kind: ByRef( - UpvarBorrow(MutBorrow, '_#34r), - ), - }, - }, - ], -} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs index fc966c4193e99..4e30194b17e48 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs @@ -19,6 +19,8 @@ fn main() { //~^ ERROR: attributes on expressions are experimental || { let [a, b, .., e] = arr; + //~^ ERROR: Capturing arr[Index] -> ByValue + //~^^ ERROR: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); assert_eq!(e, "E"); diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr index 5c6b505d64fd3..2547322a8abf2 100644 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr +++ b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: aborting due to previous error; 1 warning emitted +error: Capturing arr[Index] -> ByValue + --> $DIR/slice-pat.rs:21:33 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Min Capture arr[] -> ByValue + --> $DIR/slice-pat.rs:21:33 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout b/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout deleted file mode 100644 index 43c43015134ae..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stdout +++ /dev/null @@ -1,56 +0,0 @@ -For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): Using new-style capture analysis -For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): capture information: { - Place { - base_ty: [std::string::String; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), - ), - projections: [ - Projection { - ty: std::string::String, - kind: Index, - }, - ], - }: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ slice_pat[317d]::main), - local_id: 179, - }, - ), - capture_kind: ByValue( - Some( - $DIR/slice-pat.rs:21:33: 21:36 (#0), - ), - ), - }, -} -For closure=DefId(0:5 ~ slice_pat[317d]::main::{closure#0}): min_captures={ - HirId { - owner: DefId(0:3 ~ slice_pat[317d]::main), - local_id: 1, - }: [ - CapturedPlace { - place: Place { - base_ty: [std::string::String; 5], - base: Upvar( - UpvarId(HirId { owner: DefId(0:3 ~ slice_pat[317d]::main), local_id: 1 };`arr`;DefId(0:5 ~ slice_pat[317d]::main::{closure#0})), - ), - projections: [], - }, - info: CaptureInfo { - expr_id: Some( - HirId { - owner: DefId(0:3 ~ slice_pat[317d]::main), - local_id: 179, - }, - ), - capture_kind: ByValue( - Some( - $DIR/slice-pat.rs:21:33: 21:36 (#0), - ), - ), - }, - }, - ], -} From fa381600dcf4a5279e3222b48b4a1363d71ac7a9 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Sun, 1 Nov 2020 16:39:47 -0500 Subject: [PATCH 09/39] Handle `let _ = x` patterns in closure liveness analysis --- compiler/rustc_passes/src/liveness.rs | 69 +++++++++++++++++++-------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 7288015e17029..debb873beb93b 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -317,10 +317,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { // swap in a new set of IR maps for this body let mut maps = IrMaps::new(self.tcx); let hir_id = maps.tcx.hir().body_owner(body.id()); - let def_id = maps.tcx.hir().local_def_id(hir_id); + let local_def_id = maps.tcx.hir().local_def_id(hir_id); + let def_id = local_def_id.to_def_id(); // Don't run unused pass for #[derive()] - if let Some(parent) = self.tcx.parent(def_id.to_def_id()) { + if let Some(parent) = self.tcx.parent(def_id) { if let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) { if self.tcx.has_attr(parent, sym::automatically_derived) { return; @@ -328,8 +329,8 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { } } - if let Some(upvars) = maps.tcx.upvars_mentioned(def_id) { - for (&var_hir_id, _upvar) in upvars { + if let Some(captures) = maps.tcx.typeck(local_def_id).closure_captures.get(&def_id) { + for &var_hir_id in captures.keys() { let var_name = maps.tcx.hir().name(var_hir_id); maps.add_variable(Upvar(var_hir_id, var_name)); } @@ -340,7 +341,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { intravisit::walk_body(&mut maps, body); // compute liveness - let mut lsets = Liveness::new(&mut maps, def_id); + let mut lsets = Liveness::new(&mut maps, local_def_id); let entry_ln = lsets.compute(&body, hir_id); lsets.log_liveness(entry_ln, body.id().hir_id); @@ -397,10 +398,18 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { // construction site. let mut call_caps = Vec::new(); let closure_def_id = self.tcx.hir().local_def_id(expr.hir_id); - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - call_caps.extend(upvars.iter().map(|(&var_id, upvar)| { + if let Some(captures) = self + .tcx + .typeck(closure_def_id) + .closure_captures + .get(&closure_def_id.to_def_id()) + { + // If closure captures is Some, upvars_mentioned must also be Some + let upvars = self.tcx.upvars_mentioned(closure_def_id).unwrap(); + call_caps.extend(captures.keys().map(|var_id| { + let upvar = upvars[var_id]; let upvar_ln = self.add_live_node(UpvarNode(upvar.span)); - CaptureInfo { ln: upvar_ln, var_hid: var_id } + CaptureInfo { ln: upvar_ln, var_hid: *var_id } })); } self.set_captures(expr.hir_id, call_caps); @@ -564,6 +573,7 @@ struct Liveness<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, param_env: ty::ParamEnv<'tcx>, upvars: Option<&'tcx FxIndexMap>, + closure_captures: Option<&'tcx FxIndexMap>, successors: IndexVec>, rwu_table: RWUTable, @@ -587,6 +597,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let typeck_results = ir.tcx.typeck(body_owner); let param_env = ir.tcx.param_env(body_owner); let upvars = ir.tcx.upvars_mentioned(body_owner); + let closure_captures = typeck_results.closure_captures.get(&body_owner.to_def_id()); let closure_ln = ir.add_live_node(ClosureNode); let exit_ln = ir.add_live_node(ExitNode); @@ -600,6 +611,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { typeck_results, param_env, upvars, + closure_captures, successors: IndexVec::from_elem_n(None, num_live_nodes), rwu_table: RWUTable::new(num_live_nodes * num_vars), closure_ln, @@ -850,14 +862,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // if they are live on the entry to the closure, since only the closure // itself can access them on subsequent calls. - if let Some(upvars) = self.upvars { + if let Some(closure_captures) = self.closure_captures { // Mark upvars captured by reference as used after closure exits. - for (&var_hir_id, upvar) in upvars.iter().rev() { - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_hir_id }, - closure_expr_id: self.body_owner, - }; - match self.typeck_results.upvar_capture(upvar_id) { + // Since closure_captures is Some, upvars must exists too. + let upvars = self.upvars.unwrap(); + for (&var_hir_id, upvar_id) in closure_captures { + let upvar = upvars[&var_hir_id]; + match self.typeck_results.upvar_capture(*upvar_id) { ty::UpvarCapture::ByRef(_) => { let var = self.variable(var_hir_id, upvar.span); self.acc(self.exit_ln, var, ACC_READ | ACC_USE); @@ -869,7 +880,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let succ = self.propagate_through_expr(&body.value, self.exit_ln); - if self.upvars.is_none() { + if self.closure_captures.is_none() { // Either not a closure, or closure without any captured variables. // No need to determine liveness of captured variables, since there // are none. @@ -1341,7 +1352,21 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { acc: u32, ) -> LiveNode { match path.res { - Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span), + Res::Local(hid) => { + let in_upvars = self.upvars.map_or(false, |u| u.contains_key(&hid)); + let in_captures = self.closure_captures.map_or(false, |c| c.contains_key(&hid)); + + match (in_upvars, in_captures) { + (false, _) | (true, true) => self.access_var(hir_id, hid, succ, acc, path.span), + (true, false) => { + // This case is possible when with RFC-2229, a wild pattern + // is used within a closure. + // eg: `let _ = x`. The closure doesn't capture x here, + // even though it's mentioned in the closure. + succ + } + } + } _ => succ, } } @@ -1531,11 +1556,15 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_upvars(&self, entry_ln: LiveNode) { - let upvars = match self.upvars { + let closure_captures = match self.closure_captures { None => return, - Some(upvars) => upvars, + Some(closure_captures) => closure_captures, }; - for (&var_hir_id, upvar) in upvars.iter() { + + // If closure_captures is Some(), upvars must be Some() too. + let upvars = self.upvars.unwrap(); + for &var_hir_id in closure_captures.keys() { + let upvar = upvars[&var_hir_id]; let var = self.variable(var_hir_id, upvar.span); let upvar_id = ty::UpvarId { var_path: ty::UpvarPath { hir_id: var_hir_id }, From be77402ee3ca6a4cab16b45c8c35baa13a9d5a05 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Thu, 5 Nov 2020 15:16:24 -0500 Subject: [PATCH 10/39] More pattern testcases --- .../destructure_patterns.rs | 66 +++++++++++ .../destructure_patterns.stderr | 111 ++++++++++++++++++ .../2229_closure_analysis/slice-pat.rs | 30 ----- .../2229_closure_analysis/slice-pat.stderr | 33 ------ .../2229_closure_analysis/wild_patterns.rs | 62 ++++++++++ .../wild_patterns.stderr | 75 ++++++++++++ 6 files changed, 314 insertions(+), 63 deletions(-) create mode 100644 src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr delete mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.rs delete mode 100644 src/test/ui/closures/2229_closure_analysis/slice-pat.stderr create mode 100644 src/test/ui/closures/2229_closure_analysis/wild_patterns.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs new file mode 100644 index 0000000000000..36f258f950f4f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -0,0 +1,66 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure Index projections are handled properly during capture analysis +// The array should be moved in entirety, even though only some elements are used. +fn arrays() { + let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let [a, b, .., e] = arr; + //~^ ERROR: Capturing arr[Index] -> ByValue + //~^^ ERROR: Min Capture arr[] -> ByValue + assert_eq!(a, "A"); + assert_eq!(b, "B"); + assert_eq!(e, "E"); + }; + + c(); +} + +struct Point { + x: i32, + y: i32, + id: String, +} + +fn structs() { + let mut p = Point { x: 10, y: 10, id: String::new() }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let Point { x: ref mut x, y: _, id: moved_id } = p; + //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow + //~^^ ERROR: Capturing p[(2, 0)] -> ByValue + //~^^^ ERROR: Min Capture p[(0, 0)] -> MutBorrow + //~^^^^ ERROR: Min Capture p[(2, 0)] -> ByValue + + println!("{}, {}", x, moved_id); + }; + c(); +} + +fn tuples() { + let mut t = (10, String::new(), (String::new(), 42)); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let (ref mut x, ref ref_str, (moved_s, _)) = t; + //~^ ERROR: Capturing t[(0, 0)] -> MutBorrow + //~^^ ERROR: Capturing t[(1, 0)] -> ImmBorrow + //~^^^ ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue + //~^^^^ ERROR: Min Capture t[(0, 0)] -> MutBorrow + //~^^^^^ ERROR: Min Capture t[(1, 0)] -> ImmBorrow + //~^^^^^^ ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue + + println!("{}, {} {}", x, ref_str, moved_s); + }; + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr new file mode 100644 index 0000000000000..388cfd3da92c8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -0,0 +1,111 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:10:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:33:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/destructure_patterns.rs:50:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/destructure_patterns.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:13:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Min Capture arr[] -> ByValue + --> $DIR/destructure_patterns.rs:13:29 + | +LL | let [a, b, .., e] = arr; + | ^^^ + +error: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Capturing p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Min Capture p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Min Capture p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:36:58 + | +LL | let Point { x: ref mut x, y: _, id: moved_id } = p; + | ^ + +error: Capturing t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Capturing t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: Min Capture t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:53:54 + | +LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; + | ^ + +error: aborting due to 15 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs b/src/test/ui/closures/2229_closure_analysis/slice-pat.rs deleted file mode 100644 index 4e30194b17e48..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete -#![feature(rustc_attrs)] - -// Test to ensure Index projections are handled properly during capture analysis -// -// The array should be moved in entirety, even though only some elements are used. - -fn main() { - let arr : [String; 5] = [ - format!("A"), - format!("B"), - format!("C"), - format!("D"), - format!("E") - ]; - - let c = #[rustc_capture_analysis] - //~^ ERROR: attributes on expressions are experimental - || { - let [a, b, .., e] = arr; - //~^ ERROR: Capturing arr[Index] -> ByValue - //~^^ ERROR: Min Capture arr[] -> ByValue - assert_eq!(a, "A"); - assert_eq!(b, "B"); - assert_eq!(e, "E"); - }; - - c(); -} diff --git a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr b/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr deleted file mode 100644 index 2547322a8abf2..0000000000000 --- a/src/test/ui/closures/2229_closure_analysis/slice-pat.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0658]: attributes on expressions are experimental - --> $DIR/slice-pat.rs:18:13 - | -LL | let c = #[rustc_capture_analysis] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #15701 for more information - = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable - -warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/slice-pat.rs:1:12 - | -LL | #![feature(capture_disjoint_fields)] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53488 for more information - -error: Capturing arr[Index] -> ByValue - --> $DIR/slice-pat.rs:21:33 - | -LL | let [a, b, .., e] = arr; - | ^^^ - -error: Min Capture arr[] -> ByValue - --> $DIR/slice-pat.rs:21:33 - | -LL | let [a, b, .., e] = arr; - | ^^^ - -error: aborting due to 3 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs new file mode 100644 index 0000000000000..d17af7cc79a47 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -0,0 +1,62 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +// Test to ensure that we can handle cases where +// let statements create no bindings are intialized +// using a Place expression +// +// Note: Currently when feature `capture_disjoint_fields` is enabled +// we can't handle such cases. So the test so the test + +struct Point { + x: i32, + y: i32, +} + +fn wild_struct() { + let p = Point { x: 10, y: 20 }; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + // FIXME(arora-aman): Change `_x` to `_` + let Point { x: _x, y: _ } = p; + //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + }; + + c(); +} + +fn wild_tuple() { + let t = (String::new(), 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + // FIXME(arora-aman): Change `_x` to `_` + let (_x, _) = t; + //~^ ERROR: Capturing t[(0, 0)] -> ByValue + //~^^ ERROR: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +fn wild_arr() { + let arr = [String::new(), String::new()]; + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + // FIXME(arora-aman): Change `_x` to `_` + let [_x, _] = arr; + //~^ ERROR: Capturing arr[Index] -> ByValue + //~^^ ERROR: Min Capture arr[] -> ByValue + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr new file mode 100644 index 0000000000000..621c8aeb790f9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr @@ -0,0 +1,75 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:20:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:35:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/wild_patterns.rs:50:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/wild_patterns.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:24:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:24:37 + | +LL | let Point { x: _x, y: _ } = p; + | ^ + +error: Capturing t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:39:23 + | +LL | let (_x, _) = t; + | ^ + +error: Min Capture t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:39:23 + | +LL | let (_x, _) = t; + | ^ + +error: Capturing arr[Index] -> ByValue + --> $DIR/wild_patterns.rs:54:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: Min Capture arr[] -> ByValue + --> $DIR/wild_patterns.rs:54:23 + | +LL | let [_x, _] = arr; + | ^^^ + +error: aborting due to 9 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. From abc40040be3e10bb8228d627012c92c54d8f791f Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Thu, 5 Nov 2020 15:36:27 -0500 Subject: [PATCH 11/39] Remove local testing env var --- compiler/rustc_typeck/src/check/upvar.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index a01aa8531ca9c..2dff7d0fbddfb 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -44,8 +44,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_span::sym; use rustc_span::{Span, Symbol}; -use std::env; - /// Describe the relationship between the paths of two places /// eg: /// - foo is ancestor of foo.bar.baz @@ -127,8 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); - if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) { - } else { + if !self.tcx.features().capture_disjoint_fields { if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { for (&var_hir_id, _) in upvars.iter() { let place = self.place_for_root_variable(local_def_id, var_hir_id); From 43423f67a0f260c41181c5b2c1d25757f6b6babe Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 9 Nov 2020 00:15:45 -0500 Subject: [PATCH 12/39] Address review comments --- compiler/rustc_middle/src/ty/context.rs | 5 +- compiler/rustc_middle/src/ty/mod.rs | 35 +++-- compiler/rustc_typeck/src/check/upvar.rs | 176 +++++++++++++---------- 3 files changed, 131 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 76ca0c51ce1c3..ee577213aba14 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,9 +415,8 @@ pub struct TypeckResults<'tcx> { /// entire variable. pub closure_captures: ty::UpvarListMap, - /// Given the closure DefId this map provides a map of - /// root variables to minimum set of `Place`s (and how) that need to be tracked - /// to support all captures of that closure. + /// Tracks the minimum captures required for a closure; + /// see `MinCaptureInformationMap` for more details. pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>, /// Stores the type, expression, span and optional scope span of all types diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c41463d18448d..0c786be0478de 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -763,6 +763,31 @@ pub struct UpvarBorrow<'tcx> { pub region: ty::Region<'tcx>, } +/// Given the closure DefId this map provides a map of root variables to minimum +/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure. +pub type MinCaptureInformationMap<'tcx> = FxHashMap>; + +/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`. +/// Used to track the minimum set of `Place`s that need to be captured to support all +/// Places captured by the closure starting at a given root variable. +/// +/// This provides a convenient and quick way of checking if a variable being used within +/// a closure is a capture of a local variable. +pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; + +/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. +pub type MinCaptureList<'tcx> = Vec>; + +/// A `Place` and the corresponding `CaptureInfo`. +#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct CapturedPlace<'tcx> { + pub place: HirPlace<'tcx>, + pub info: CaptureInfo<'tcx>, +} + +/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) +/// for a particular capture as well as identifying the part of the source code +/// that triggered this capture to occur. #[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)] pub struct CaptureInfo<'tcx> { /// Expr Id pointing to use that resulted in selecting the current capture kind @@ -788,19 +813,9 @@ pub struct CaptureInfo<'tcx> { pub capture_kind: UpvarCapture<'tcx>, } -#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct CapturedPlace<'tcx> { - pub place: HirPlace<'tcx>, - pub info: CaptureInfo<'tcx>, -} - pub type UpvarListMap = FxHashMap>; pub type UpvarCaptureMap<'tcx> = FxHashMap>; -pub type MinCaptureList<'tcx> = Vec>; -pub type RootVariableMinCaptureList<'tcx> = FxIndexMap>; -pub type MinCaptureInformationMap<'tcx> = FxHashMap>; - #[derive(Clone, Copy, PartialEq, Eq)] pub enum IntVarValue { IntType(ast::IntTy), diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 2dff7d0fbddfb..90e021ae592a5 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -46,9 +46,9 @@ use rustc_span::{Span, Symbol}; /// Describe the relationship between the paths of two places /// eg: -/// - foo is ancestor of foo.bar.baz -/// - foo.bar.baz is an descendant of foo.bar, -/// - foo.bar and foo.baz are divergent +/// - `foo` is ancestor of `foo.bar.baz` +/// - `foo.bar.baz` is an descendant of `foo.bar` +/// - `foo.bar` and `foo.baz` are divergent enum PlaceAncestryRelation { Ancestor, Descendant, @@ -124,7 +124,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); - let mut capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); + let mut capture_information: FxIndexMap, ty::CaptureInfo<'tcx>> = + Default::default(); if !self.tcx.features().capture_disjoint_fields { if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { for (&var_hir_id, _) in upvars.iter() { @@ -186,7 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.compute_min_captures(closure_def_id, delegate); self.log_closure_min_capture_info(closure_def_id, span); - self.set_closure_captures(closure_def_id); + self.min_captures_to_closure_captures_bridge(closure_def_id); // Now that we've analyzed the closure, we know how each // variable is borrowed, and we know what traits the closure @@ -274,8 +275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// 2. upvar_capture_map /// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue - - fn set_closure_captures(&self, closure_def_id: DefId) { + fn min_captures_to_closure_captures_bridge(&self, closure_def_id: DefId) { let mut closure_captures: FxIndexMap = Default::default(); let mut upvar_capture_map = ty::UpvarCaptureMap::default(); @@ -304,8 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we create a fake capture info with no expression. let fake_capture_info = ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() }; - self.determine_capture_info(fake_capture_info, capture_info.clone()) - .capture_kind + determine_capture_info(fake_capture_info, capture_info).capture_kind } else { capture_info.capture_kind }; @@ -329,7 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Analyses the information collected by InferBorrowKind to compute the min number of + /// Analyzes the information collected by `InferBorrowKind` to compute the min number of /// Places (and corresponding capture kind) that we need to keep track of to support all /// the required captured paths. /// @@ -420,8 +419,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // current place is ancestor of possible_descendant PlaceAncestryRelation::Ancestor => { descendant_found = true; - updated_capture_info = self - .determine_capture_info(updated_capture_info, possible_descendant.info); + updated_capture_info = + determine_capture_info(updated_capture_info, possible_descendant.info); false } @@ -437,7 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceAncestryRelation::Descendant => { ancestor_found = true; possible_ancestor.info = - self.determine_capture_info(possible_ancestor.info, capture_info); + determine_capture_info(possible_ancestor.info, capture_info); // Only one ancestor of the current place will be in the list. break; @@ -500,60 +499,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } - /// Helper function to determine if we need to escalate CaptureKind from - /// CaptureInfo A to B and returns the escalated CaptureInfo. - /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) - /// - /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based - /// on the `CaptureInfo` containing an associated expression id. - /// - /// If both the CaptureKind and Expression are considered to be equivalent, - /// then `CaptureInfo` A is preferred. - fn determine_capture_info( - &self, - capture_info_a: ty::CaptureInfo<'tcx>, - capture_info_b: ty::CaptureInfo<'tcx>, - ) -> ty::CaptureInfo<'tcx> { - // If the capture kind is equivalent then, we don't need to escalate and can compare the - // expressions. - let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => true, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - ref_a.kind == ref_b.kind - } - _ => false, - }; - - if eq_capture_kind { - match (capture_info_a.expr_id, capture_info_b.expr_id) { - (Some(_), _) | (None, None) => capture_info_a, - (None, Some(_)) => capture_info_b, - } - } else { - match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue(_), _) => capture_info_a, - (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - match (ref_a.kind, ref_b.kind) { - // Take LHS: - (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) - | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, - - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, - - (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) - | (ty::MutBorrow, ty::MutBorrow) => { - bug!("Expected unequal capture kinds"); - } - } - } - } - } - } - fn log_closure_capture_info( &self, closure_def_id: rustc_hir::def_id::DefId, @@ -617,8 +562,9 @@ struct InferBorrowKind<'a, 'tcx> { // variable access that caused us to do so. current_origin: Option<(Span, Symbol)>, - /// For each Place that we access, we track the minimal kind of + /// For each Place that is captured by the closure, we track the minimal kind of /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. + /// /// Consider closure where s.str1 is captured via an ImmutableBorrow and /// s.str2 via a MutableBorrow /// @@ -686,7 +632,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { }; let curr_info = self.capture_information[&place_with_id.place]; - let updated_info = self.fcx.determine_capture_info(curr_info, capture_info); + let updated_info = determine_capture_info(curr_info, capture_info); self.capture_information[&place_with_id.place] = updated_info; } @@ -804,7 +750,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { expr_id: Some(diag_expr_id), capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow), }; - let updated_info = self.fcx.determine_capture_info(curr_capture_info, capture_info); + let updated_info = determine_capture_info(curr_capture_info, capture_info); self.capture_information[&place_with_id.place] = updated_info; }; } @@ -859,14 +805,14 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); - debug!("Capturing new place {:?}", place_with_id); - let capture_kind = self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span); let expr_id = Some(diag_expr_id); let capture_info = ty::CaptureInfo { expr_id, capture_kind }; + debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info); + self.capture_information.insert(place_with_id.place.clone(), capture_info); } else { debug!("Not upvar: {:?}", place_with_id); @@ -964,6 +910,92 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } +/// Helper function to determine if we need to escalate CaptureKind from +/// CaptureInfo A to B and returns the escalated CaptureInfo. +/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) +/// +/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based +/// on the `CaptureInfo` containing an associated expression id. +/// +/// If both the CaptureKind and Expression are considered to be equivalent, +/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize +/// expressions reported back to the user as part of diagnostics based on which appears earlier +/// in the closure. This can be acheived simply by calling +/// `determine_capture_info(existing_info, current_info)`. This works out because the +/// expressions that occur earlier in the closure body than the current expression are processed before. +/// Consider the following example +/// ```rust +/// let mut p: Point { x: 10, y: 10 }; +/// +/// let c = || { +/// p.x += 10; +/// // ^ E1 ^ +/// // ... +/// // More code +/// // ... +/// p.x += 10; // E2 +/// // ^ E2 ^ +/// } +/// ``` +/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), +/// and both have an expression associated, however for diagnostics we prfer reporting +/// `E1` since it appears earlier in the closure body. When `E2` is being processed we +/// would've already handled `E1`, and have an existing capture_information for it. +/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return +/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. +fn determine_capture_info( + capture_info_a: ty::CaptureInfo<'tcx>, + capture_info_b: ty::CaptureInfo<'tcx>, +) -> ty::CaptureInfo<'tcx> { + // If the capture kind is equivalent then, we don't need to escalate and can compare the + // expressions. + let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { + // We don't need to worry about the spans being ignored here. + // + // The expr_id in capture_info corresponds to the span that is stored within + // ByValue(span) and therefore it gets handled with priortizing based on + // expressions below. + true + } + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + ref_a.kind == ref_b.kind + } + (ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false, + }; + + if eq_capture_kind { + match (capture_info_a.expr_id, capture_info_b.expr_id) { + (Some(_), _) | (None, None) => capture_info_a, + (None, Some(_)) => capture_info_b, + } + } else { + // We select the CaptureKind which ranks higher based the following priority order: + // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow + match (capture_info_a.capture_kind, capture_info_b.capture_kind) { + (ty::UpvarCapture::ByValue(_), _) => capture_info_a, + (_, ty::UpvarCapture::ByValue(_)) => capture_info_b, + (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { + match (ref_a.kind, ref_b.kind) { + // Take LHS: + (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) + | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, + + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) + | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, + + (ty::ImmBorrow, ty::ImmBorrow) + | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) + | (ty::MutBorrow, ty::MutBorrow) => { + bug!("Expected unequal capture kinds"); + } + } + } + } + } +} + /// Determines the Ancestry relationship of Place A relative to Place B /// /// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B From deeb025f39a39fc58fc87900d62550dada849e1b Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 10 Nov 2020 03:05:50 -0500 Subject: [PATCH 13/39] Address review comments 2 --- compiler/rustc_typeck/src/check/upvar.rs | 2 +- .../arrays-completely-captured.rs | 2 +- .../capture-disjoint-field-struct.rs | 2 +- .../capture-disjoint-field-tuple.rs | 2 +- .../destructure_patterns.rs | 18 +++++++++--------- .../feature-gate-capture_disjoint_fields.rs | 2 +- .../filter-on-struct-member.rs | 2 +- .../2229_closure_analysis/multilevel-path-1.rs | 2 +- .../2229_closure_analysis/multilevel-path-2.rs | 2 +- .../2229_closure_analysis/nested-closure.rs | 12 ++++++------ .../path-with-array-access.rs | 2 +- .../simple-struct-min-capture.rs | 2 +- .../2229_closure_analysis/wild_patterns.rs | 6 +++--- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 90e021ae592a5..87365de90470d 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -938,7 +938,7 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { /// } /// ``` /// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), -/// and both have an expression associated, however for diagnostics we prfer reporting +/// and both have an expression associated, however for diagnostics we prefer reporting /// `E1` since it appears earlier in the closure body. When `E2` is being processed we /// would've already handled `E1`, and have an existing capture_information for it. /// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index e144cce54ec22..01c28aa29fb8e 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -11,7 +11,7 @@ fn main() { || { m[0] += 10; //~^ ERROR: Capturing m[] -> MutBorrow - //~^^ ERROR: Min Capture m[] -> MutBorrow + //~| ERROR: Min Capture m[] -> MutBorrow m[1] += 40; }; diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index 3f2e16725dc38..b50c2d66d94b4 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -17,7 +17,7 @@ fn main() { || { println!("{}", p.x); //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow }; // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index 5db1c9c0683ec..99095c7198663 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -12,7 +12,7 @@ fn main() { || { println!("{}", t.0); //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture t[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture t[(0, 0)] -> ImmBorrow }; // `c` only captures t.0, therefore mutating t.1 is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs index 36f258f950f4f..eef8fd6e55748 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -12,7 +12,7 @@ fn arrays() { || { let [a, b, .., e] = arr; //~^ ERROR: Capturing arr[Index] -> ByValue - //~^^ ERROR: Min Capture arr[] -> ByValue + //~| ERROR: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); assert_eq!(e, "E"); @@ -35,9 +35,9 @@ fn structs() { || { let Point { x: ref mut x, y: _, id: moved_id } = p; //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~^^ ERROR: Capturing p[(2, 0)] -> ByValue - //~^^^ ERROR: Min Capture p[(0, 0)] -> MutBorrow - //~^^^^ ERROR: Min Capture p[(2, 0)] -> ByValue + //~| ERROR: Capturing p[(2, 0)] -> ByValue + //~| ERROR: Min Capture p[(0, 0)] -> MutBorrow + //~| ERROR: Min Capture p[(2, 0)] -> ByValue println!("{}, {}", x, moved_id); }; @@ -52,11 +52,11 @@ fn tuples() { || { let (ref mut x, ref ref_str, (moved_s, _)) = t; //~^ ERROR: Capturing t[(0, 0)] -> MutBorrow - //~^^ ERROR: Capturing t[(1, 0)] -> ImmBorrow - //~^^^ ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue - //~^^^^ ERROR: Min Capture t[(0, 0)] -> MutBorrow - //~^^^^^ ERROR: Min Capture t[(1, 0)] -> ImmBorrow - //~^^^^^^ ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue + //~| ERROR: Capturing t[(1, 0)] -> ImmBorrow + //~| ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue + //~| ERROR: Min Capture t[(0, 0)] -> MutBorrow + //~| ERROR: Min Capture t[(1, 0)] -> ImmBorrow + //~| ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue println!("{}, {} {}", x, ref_str, moved_s); }; diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index 3713abe52df20..ee55e3a3f2181 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -10,6 +10,6 @@ fn main() { || { println!("This uses new capture analyysis to capture s={}", s); //~^ ERROR: Capturing s[] -> ImmBorrow - //~^^ ERROR: Min Capture s[] -> ImmBorrow + //~| ERROR: Min Capture s[] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs index 3ef8b4acf9782..d526934271c4b 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -25,7 +25,7 @@ impl Data { #[rustc_capture_analysis] |v| self.filter.allowed(*v), //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow ); } } diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs index a6793a43488e8..08c9aa8eff849 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -24,7 +24,7 @@ fn main() { || { let wp = &w.p; //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture w[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture w[(0, 0)] -> ImmBorrow println!("{}", wp.x); }; diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs index 994e2530f7825..020753a71bf3f 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -21,7 +21,7 @@ fn main() { || { println!("{}", w.p.x); //~^ ERROR: Capturing w[(0, 0),(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow }; // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index 8641d6b4f59fb..d2e99fe4accf6 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -23,17 +23,17 @@ fn main() { || { println!("{}", p.x); //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow let incr = 10; let mut c2 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental || p.y += incr; //~^ ERROR: Capturing p[(1, 0)] -> MutBorrow - //~^^ ERROR: Capturing incr[] -> ImmBorrow - //~^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow - //~^^^^ ERROR: Min Capture incr[] -> ImmBorrow - //~^^^^^ ERROR: Capturing p[(1, 0)] -> MutBorrow - //~^^^^^^ ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~| ERROR: Capturing incr[] -> ImmBorrow + //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~| ERROR: Min Capture incr[] -> ImmBorrow + //~| ERROR: Capturing p[(1, 0)] -> MutBorrow + //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); }; diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs index 48e0698b4a750..4a42970137fff 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -25,6 +25,6 @@ fn main() { || { println!("{}", pent.points[5].x); //~^ ERROR: Capturing pent[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture pent[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture pent[(0, 0)] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs index 3d5a08344b72a..68c18eac8048e 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -27,7 +27,7 @@ fn main() { || { p.x += 10; //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~^^ ERROR: Min Capture p[] -> MutBorrow + //~| ERROR: Min Capture p[] -> MutBorrow println!("{:?}", p); //~^ ERROR: Capturing p[] -> ImmBorrow }; diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs index d17af7cc79a47..889836c11ce88 100644 --- a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -23,7 +23,7 @@ fn wild_struct() { // FIXME(arora-aman): Change `_x` to `_` let Point { x: _x, y: _ } = p; //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow }; c(); @@ -38,7 +38,7 @@ fn wild_tuple() { // FIXME(arora-aman): Change `_x` to `_` let (_x, _) = t; //~^ ERROR: Capturing t[(0, 0)] -> ByValue - //~^^ ERROR: Min Capture t[(0, 0)] -> ByValue + //~| ERROR: Min Capture t[(0, 0)] -> ByValue }; c(); @@ -53,7 +53,7 @@ fn wild_arr() { // FIXME(arora-aman): Change `_x` to `_` let [_x, _] = arr; //~^ ERROR: Capturing arr[Index] -> ByValue - //~^^ ERROR: Min Capture arr[] -> ByValue + //~| ERROR: Min Capture arr[] -> ByValue }; c(); From d0fac05d8f0920d0aaa81dc4832334a49e7dbff8 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 10 Nov 2020 22:54:04 -0500 Subject: [PATCH 14/39] Add test for capturing enums --- .../2229_closure_analysis/capture-enums.rs | 56 +++++++++++++ .../capture-enums.stderr | 78 +++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-enums.rs create mode 100644 src/test/ui/closures/2229_closure_analysis/capture-enums.stderr diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs new file mode 100644 index 0000000000000..175fd79012446 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs @@ -0,0 +1,56 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING the feature `capture_disjoint_fields` is incomplete +#![feature(rustc_attrs)] + +enum Info { + Point(i32, i32, String), + Meta(String, Vec<(i32, i32)>) +} + +fn multi_variant_enum() { + let point = Info::Point(10, -10, "1".into()); + + let vec = Vec::new(); + let meta = Info::Meta("meta".into(), vec); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + if let Info::Point(_, _, str) = point { + //~^ Capturing point[] -> ImmBorrow + //~| Capturing point[(2, 0)] -> ByValue + //~| Min Capture point[] -> ByValue + println!("{}", str); + } + + if let Info::Meta(_, v) = meta { + //~^ Capturing meta[] -> ImmBorrow + //~| Capturing meta[(1, 1)] -> ByValue + //~| Min Capture meta[] -> ByValue + println!("{:?}", v); + } + }; + + c(); +} + +enum SingleVariant { + Point(i32, i32, String), +} + +fn single_variant_enum() { + let point = SingleVariant::Point(10, -10, "1".into()); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + || { + let SingleVariant::Point(_, _, str) = point; + //~^ Capturing point[(2, 0)] -> ByValue + //~| Min Capture point[(2, 0)] -> ByValue + println!("{}", str); + }; + + c(); +} + +fn main() {} diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr new file mode 100644 index 0000000000000..76a2de2faf9c8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -0,0 +1,78 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:16:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-enums.rs:44:13 + | +LL | let c = #[rustc_capture_analysis] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-enums.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: Capturing point[] -> ImmBorrow + --> $DIR/capture-enums.rs:19:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ + +error: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:19:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ + +error: Capturing meta[] -> ImmBorrow + --> $DIR/capture-enums.rs:26:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Capturing meta[(1, 1)] -> ByValue + --> $DIR/capture-enums.rs:26:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Min Capture point[] -> ByValue + --> $DIR/capture-enums.rs:19:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ + +error: Min Capture meta[] -> ByValue + --> $DIR/capture-enums.rs:26:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ + +error: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:47:43 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: Min Capture point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:47:43 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ + +error: aborting due to 10 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`. From 443b45fa9fd87cf0939e80a64bead413530e375c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 11 Nov 2020 20:10:15 +0300 Subject: [PATCH 15/39] rustc_target: Change os from "unknown" to "none" for bare metal targets x86_64-fortanix-unknown-sgx and wasm32-unknown-unknown still have os == "unknown" because both have libstd --- compiler/rustc_target/src/spec/avr_gnu_base.rs | 1 - compiler/rustc_target/src/spec/mod.rs | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 9cc10032c71da..67a7684da2c70 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -11,7 +11,6 @@ pub fn target(target_cpu: String) -> Target { pointer_width: 16, options: TargetOptions { c_int_width: "16".to_string(), - os: "unknown".to_string(), cpu: target_cpu.clone(), exe_suffix: ".elf".to_string(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 2a4ae786fb752..dc8c41ecedc2c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -713,6 +713,9 @@ pub struct TargetOptions { /// Width of c_int type. Defaults to "32". pub c_int_width: String, /// OS name to use for conditional compilation. Defaults to "none". + /// "none" implies a bare metal target without `std` library. + /// A couple of targets having `std` also use "unknown" as an `os` value, + /// but they are exceptions. pub os: String, /// Environment name to use for conditional compilation. Defaults to "". pub env: String, From 1def24c5f45df9f8d101cdaec4b6485aa9ad1aae Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 11 Nov 2020 20:40:51 +0300 Subject: [PATCH 16/39] rustc_target: Normalize vendor from "" to "unknown" for all targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Majority of targets use "unknown" vendor and changing it from "unknown" to omitted doesn't make sense. From the LLVM docs (https://clang.llvm.org/docs/CrossCompilation.html#target-triple): >Most of the time it can be omitted (and Unknown) will be assumed, which sets the defaults for the specified architecture. >When a parameter is not important, it can be omitted, or you can choose unknown and the defaults will be used. If you choose a parameter that Clang doesn’t know, like blerg, it’ll ignore and assume unknown --- compiler/rustc_target/src/spec/aarch64_unknown_none.rs | 1 - .../rustc_target/src/spec/aarch64_unknown_none_softfloat.rs | 1 - compiler/rustc_target/src/spec/armebv7r_none_eabi.rs | 1 - compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs | 1 - compiler/rustc_target/src/spec/armv7a_none_eabi.rs | 4 ---- compiler/rustc_target/src/spec/armv7a_none_eabihf.rs | 1 - compiler/rustc_target/src/spec/armv7r_none_eabi.rs | 1 - compiler/rustc_target/src/spec/armv7r_none_eabihf.rs | 1 - compiler/rustc_target/src/spec/fuchsia_base.rs | 1 - compiler/rustc_target/src/spec/mipsel_unknown_none.rs | 1 - compiler/rustc_target/src/spec/mod.rs | 6 +++--- compiler/rustc_target/src/spec/msp430_none_elf.rs | 1 - compiler/rustc_target/src/spec/thumb_base.rs | 1 - compiler/rustc_target/src/spec/wasm32_wasi.rs | 1 - 14 files changed, 3 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index d0ad45153d677..c9f622820dea7 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -10,7 +10,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+strict-align,+neon,+fp-armv8".to_string(), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs index 41bd2182905c9..0811871c993aa 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -10,7 +10,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+strict-align,-neon,-fp-armv8".to_string(), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index 3685630572371..c6586b79b87f8 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -12,7 +12,6 @@ pub fn target() -> Target { options: TargetOptions { endian: "big".to_string(), - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index 2ff3c8950c483..e3d4397f6123f 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -12,7 +12,6 @@ pub fn target() -> Target { options: TargetOptions { endian: "big".to_string(), - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs index 742b403cff90c..74deab0191652 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs @@ -10,9 +10,6 @@ // bare-metal binaries (the `gcc` linker has the advantage that it knows where C // libraries and crt*.o are but it's not much of an advantage here); LLD is also // faster -// - `os` set to `none`. rationale: matches `thumb` targets -// - `env` and `vendor` are set to an empty string. rationale: matches `thumb` -// targets // - `panic_strategy` set to `abort`. rationale: matches `thumb` targets // - `relocation-model` set to `static`; also no PIE, no relro and no dynamic // linking. rationale: matches `thumb` targets @@ -21,7 +18,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+v7,+thumb2,+soft-float,-neon,+strict-align".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index b9cda18d6b46f..c5c720f5fbde4 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -9,7 +9,6 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOp pub fn target() -> Target { let opts = TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".to_string(), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index 440c2434907be..3f49bd8786937 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index c1bf332a72ddf..9b2e8a8058fe7 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { arch: "arm".to_string(), options: TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index e467c7c8f21e9..5c39773cbe381 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -21,7 +21,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "fuchsia".to_string(), - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), lld_flavor: LldFlavor::Ld, diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs index a8005927a7beb..0f9d3c3de1543 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { arch: "mips".to_string(), options: TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float,+noabicalls".to_string(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index dc8c41ecedc2c..129cab6fa4493 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -712,14 +712,14 @@ pub struct TargetOptions { pub endian: String, /// Width of c_int type. Defaults to "32". pub c_int_width: String, - /// OS name to use for conditional compilation. Defaults to "none". + /// OS name to use for conditional compilation (`target_os`). Defaults to "none". /// "none" implies a bare metal target without `std` library. /// A couple of targets having `std` also use "unknown" as an `os` value, /// but they are exceptions. pub os: String, - /// Environment name to use for conditional compilation. Defaults to "". + /// Environment name to use for conditional compilation (`target_env`). Defaults to "". pub env: String, - /// Vendor name to use for conditional compilation. Defaults to "unknown". + /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown". pub vendor: String, /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed /// on the command line. Defaults to `LinkerFlavor::Gcc`. diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs index ef966cb702ec4..cc2578aa578e8 100644 --- a/compiler/rustc_target/src/spec/msp430_none_elf.rs +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -9,7 +9,6 @@ pub fn target() -> Target { options: TargetOptions { c_int_width: "16".to_string(), - vendor: String::new(), executables: true, // The LLVM backend currently can't generate object files. To diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index e550467502754..ec24807fec4ea 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -32,7 +32,6 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOpti pub fn opts() -> TargetOptions { // See rust-lang/rfcs#1645 for a discussion about these defaults TargetOptions { - vendor: String::new(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, // In most cases, LLD is good enough diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 9c697674f397a..3f44acdc36b2d 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -79,7 +79,6 @@ pub fn target() -> Target { let mut options = wasm32_base::options(); options.os = "wasi".to_string(); - options.vendor = String::new(); options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); options .pre_link_args From e0a8f22053868982be59a82865fa11f4fbdf6af0 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 11 Nov 2020 20:59:37 +0300 Subject: [PATCH 17/39] rustc_target: Make sure that in-tree targets follow conventions for os and vendor values --- compiler/rustc_target/src/spec/tests/tests_impl.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index f348df7d5a716..bde7cb09bdf82 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -36,5 +36,18 @@ impl Target { && self.post_link_objects_fallback.is_empty()) || self.crt_objects_fallback.is_some() ); + // Keep the default "unknown" vendor instead. + assert_ne!(self.vendor, ""); + if !self.can_use_os_unknown() { + // Keep the default "none" for bare metal targets instead. + assert_ne!(self.os, "unknown"); + } + } + + // Add your target to the whitelist if it has `std` library + // and you certainly want "unknown" for the OS name. + fn can_use_os_unknown(&self) -> bool { + self.llvm_target == "wasm32-unknown-unknown" + || (self.env == "sgx" && self.vendor == "fortanix") } } From f5e67b5ee1d442ddfc580224c69f260a07b8840b Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Thu, 12 Nov 2020 01:05:27 +0100 Subject: [PATCH 18/39] Add a test for r# identifiers --- .../rustdoc/raw-ident-eliminate-r-hashtag.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs new file mode 100644 index 0000000000000..2221b5d47acf9 --- /dev/null +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -0,0 +1,17 @@ +pub mod internal { + pub struct r#mod; + + /// See [name], [other name] + /// + /// [name]: mod + /// [other name]: ../internal/struct.mod.html + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + pub struct B; +} + +/// See [name]. +/// +/// [name]: internal::mod +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +struct A; From 9c7069645cf919cb0a848175a208d6eb4d92802e Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Thu, 12 Nov 2020 01:39:06 +0100 Subject: [PATCH 19/39] Ignore tidy linelength --- src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index 2221b5d47acf9..d758d9936132f 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + pub mod internal { pub struct r#mod; From bd0eb07af2deaff2c9f1e7553ea97341787779cd Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 2 Nov 2020 21:32:48 -0800 Subject: [PATCH 20/39] Added some unit tests as requested As discussed in PR #78267, for example: * https://github.com/rust-lang/rust/pull/78267#discussion_r515404722 * https://github.com/rust-lang/rust/pull/78267#discussion_r515405958 --- Cargo.lock | 8 + compiler/rustc_mir/Cargo.toml | 3 + .../src/transform/coverage/counters.rs | 4 +- .../rustc_mir/src/transform/coverage/debug.rs | 16 +- .../rustc_mir/src/transform/coverage/graph.rs | 16 +- .../rustc_mir/src/transform/coverage/mod.rs | 5 +- .../rustc_mir/src/transform/coverage/spans.rs | 6 +- .../transform/coverage/test_macros/Cargo.toml | 12 + .../transform/coverage/test_macros/src/lib.rs | 8 + .../rustc_mir/src/transform/coverage/tests.rs | 631 ++++++++++++++++++ 10 files changed, 687 insertions(+), 22 deletions(-) create mode 100644 compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml create mode 100644 compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 45a19fd79634e..4ab46a9cc871b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,6 +721,13 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" +[[package]] +name = "coverage_test_macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", +] + [[package]] name = "cpuid-bool" version = "0.1.2" @@ -3922,6 +3929,7 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ + "coverage_test_macros", "either", "itertools 0.9.0", "polonius-engine", diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index 487668cfa1109..9bfd1da039120 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -31,3 +31,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } rustc_apfloat = { path = "../rustc_apfloat" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } + +[dev-dependencies] +coverage_test_macros = { path = "src/transform/coverage/test_macros" } diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index d6c2f7f7aaf1d..7c4b30ca9e790 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::coverage::*; /// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR /// `Coverage` statements. -pub(crate) struct CoverageCounters { +pub(super) struct CoverageCounters { function_source_hash: u64, next_counter_id: u32, num_expressions: u32, @@ -37,7 +37,7 @@ impl CoverageCounters { self.debug_counters.enable(); } - /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlocks` directly or + /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or /// indirectly associated with `CoverageSpans`, and returns additional `Expression`s /// representing intermediate values. pub fn make_bcb_counters( diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index ffa795134e257..7f1dc3844b21d 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -127,7 +127,7 @@ pub const NESTED_INDENT: &str = " "; const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; -pub(crate) fn debug_options<'a>() -> &'a DebugOptions { +pub(super) fn debug_options<'a>() -> &'a DebugOptions { static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env()) @@ -136,7 +136,7 @@ pub(crate) fn debug_options<'a>() -> &'a DebugOptions { /// Parses and maintains coverage-specific debug options captured from the environment variable /// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. #[derive(Debug, Clone)] -pub(crate) struct DebugOptions { +pub(super) struct DebugOptions { pub allow_unused_expressions: bool, counter_format: ExpressionFormat, } @@ -250,7 +250,7 @@ impl Default for ExpressionFormat { /// /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. -pub(crate) struct DebugCounters { +pub(super) struct DebugCounters { some_counters: Option>, } @@ -386,7 +386,7 @@ impl DebugCounter { /// If enabled, this data structure captures additional debugging information used when generating /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. -pub(crate) struct GraphvizData { +pub(super) struct GraphvizData { some_bcb_to_coverage_spans_with_counters: Option>>, some_bcb_to_dependency_counters: Option>>, @@ -496,7 +496,7 @@ impl GraphvizData { /// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs /// and/or a `CoverageGraph` graphviz output). -pub(crate) struct UsedExpressions { +pub(super) struct UsedExpressions { some_used_expression_operands: Option>>, some_unused_expressions: @@ -626,7 +626,7 @@ impl UsedExpressions { } /// Generates the MIR pass `CoverageSpan`-specific spanview dump file. -pub(crate) fn dump_coverage_spanview( +pub(super) fn dump_coverage_spanview( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, basic_coverage_blocks: &CoverageGraph, @@ -666,7 +666,7 @@ fn span_viewables( } /// Generates the MIR pass coverage-specific graphviz dump file. -pub(crate) fn dump_coverage_graphviz( +pub(super) fn dump_coverage_graphviz( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, pass_name: &str, @@ -815,7 +815,7 @@ fn bcb_to_string_sections( /// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any /// values it might hold. -pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { +pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { match kind { TerminatorKind::Goto { .. } => "Goto", TerminatorKind::SwitchInt { .. } => "SwitchInt", diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index c2ed2cbb10002..9d375633dcf51 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -17,7 +17,8 @@ const ID_SEPARATOR: &str = ","; /// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional /// set of additional counters--if needed--to count incoming edges, if there are more than one. /// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.) -pub(crate) struct CoverageGraph { +#[derive(Debug)] +pub(super) struct CoverageGraph { bcbs: IndexVec, bb_to_bcb: IndexVec>, pub successors: IndexVec>, @@ -275,7 +276,7 @@ impl graph::WithPredecessors for CoverageGraph { rustc_index::newtype_index! { /// A node in the [control-flow graph][CFG] of CoverageGraph. - pub(crate) struct BasicCoverageBlock { + pub(super) struct BasicCoverageBlock { DEBUG_FORMAT = "bcb{}", } } @@ -305,7 +306,7 @@ rustc_index::newtype_index! { /// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) /// significance. #[derive(Debug, Clone)] -pub(crate) struct BasicCoverageBlockData { +pub(super) struct BasicCoverageBlockData { pub basic_blocks: Vec, pub counter_kind: Option, edge_from_bcbs: Option>, @@ -431,7 +432,7 @@ impl BasicCoverageBlockData { /// the specific branching BCB, representing the edge between the two. The latter case /// distinguishes this incoming edge from other incoming edges to the same `target_bcb`. #[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) struct BcbBranch { +pub(super) struct BcbBranch { pub edge_from_bcb: Option, pub target_bcb: BasicCoverageBlock, } @@ -498,9 +499,8 @@ fn bcb_filtered_successors<'a, 'tcx>( /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the /// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that /// ensures a loop is completely traversed before processing Blocks after the end of the loop. -// FIXME(richkadel): Add unit tests for TraversalContext. #[derive(Debug)] -pub(crate) struct TraversalContext { +pub(super) struct TraversalContext { /// From one or more backedges returning to a loop header. pub loop_backedges: Option<(Vec, BasicCoverageBlock)>, @@ -510,7 +510,7 @@ pub(crate) struct TraversalContext { pub worklist: Vec, } -pub(crate) struct TraverseCoverageGraphWithLoops { +pub(super) struct TraverseCoverageGraphWithLoops { pub backedges: IndexVec>, pub context_stack: Vec, visited: BitSet, @@ -642,7 +642,7 @@ impl TraverseCoverageGraphWithLoops { } } -fn find_loop_backedges( +pub(super) fn find_loop_backedges( basic_coverage_blocks: &CoverageGraph, ) -> IndexVec> { let num_bcbs = basic_coverage_blocks.num_nodes(); diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index c55349239b034..de37a67a1742f 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -5,6 +5,9 @@ mod debug; mod graph; mod spans; +#[cfg(test)] +mod tests; + use counters::CoverageCounters; use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use spans::{CoverageSpan, CoverageSpans}; @@ -31,7 +34,7 @@ use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; /// A simple error message wrapper for `coverage::Error`s. #[derive(Debug)] -pub(crate) struct Error { +pub(self) struct Error { message: String, } diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index cda4fc125442f..f880d69bd64f6 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -17,7 +17,7 @@ use rustc_span::{BytePos, Span, SyntaxContext}; use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] -pub(crate) enum CoverageStatement { +pub(super) enum CoverageStatement { Statement(BasicBlock, Span, usize), Terminator(BasicBlock, Span), } @@ -66,7 +66,7 @@ impl CoverageStatement { /// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` /// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. #[derive(Debug, Clone)] -pub(crate) struct CoverageSpan { +pub(super) struct CoverageSpan { pub span: Span, pub bcb: BasicCoverageBlock, pub coverage_statements: Vec, @@ -214,7 +214,7 @@ pub struct CoverageSpans<'a, 'tcx> { } impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { - pub(crate) fn generate_coverage_spans( + pub(super) fn generate_coverage_spans( mir_body: &'a mir::Body<'tcx>, body_span: Span, basic_coverage_blocks: &'a CoverageGraph, diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml new file mode 100644 index 0000000000000..a9d6f0c803d2e --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["The Rust Project Developers"] +name = "coverage_test_macros" +version = "0.0.0" +edition = "2018" + +[lib] +proc-macro = true +doctest = false + +[dependencies] +proc-macro2 = "1" diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs new file mode 100644 index 0000000000000..ea551c7745556 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs @@ -0,0 +1,8 @@ +use proc_macro::TokenStream; + +#[proc_macro] +pub fn let_bcb(item: TokenStream) -> TokenStream { + format!("let bcb{} = graph::BasicCoverageBlock::from_usize({}); let _ = {};", item, item, item) + .parse() + .unwrap() +} diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs new file mode 100644 index 0000000000000..2231fe6427fa2 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -0,0 +1,631 @@ +use super::debug; +use super::graph; + +use coverage_test_macros::let_bcb; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_data_structures::graph::WithSuccessors; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyS}; +use rustc_span::DUMMY_SP; + +use std::lazy::SyncOnceCell; + +fn dummy_ty<'tcx>() -> &'static TyS<'tcx> { + static DUMMY_TYS: SyncOnceCell> = SyncOnceCell::new(); + + &DUMMY_TYS.get_or_init(|| { + let fake_type_bytes = vec![0 as u8; std::mem::size_of::>()]; + unsafe { std::ptr::read_unaligned::>(fake_type_bytes.as_ptr() as *const TyS<'_>) } + }) +} + +struct MockBlocks<'tcx> { + blocks: IndexVec>, + source_info: SourceInfo, + dummy_place: Place<'tcx>, + next_local: usize, +} + +impl<'tcx> MockBlocks<'tcx> { + fn new() -> Self { + Self { + blocks: IndexVec::new(), + source_info: SourceInfo::outermost(DUMMY_SP), + dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() }, + next_local: 0, + } + } + + fn new_temp(&mut self) -> Local { + let index = self.next_local; + self.next_local += 1; + Local::new(index) + } + + fn push(&mut self, num_nops: usize, kind: TerminatorKind<'tcx>) -> BasicBlock { + let nop = Statement { source_info: self.source_info, kind: StatementKind::Nop }; + + self.blocks.push(BasicBlockData { + statements: std::iter::repeat(&nop).cloned().take(num_nops).collect(), + terminator: Some(Terminator { source_info: self.source_info, kind }), + is_cleanup: false, + }) + } + + fn link(&mut self, from_block: BasicBlock, to_block: BasicBlock) { + match self.blocks[from_block].terminator_mut().kind { + TerminatorKind::Assert { ref mut target, .. } + | TerminatorKind::Call { destination: Some((_, ref mut target)), .. } + | TerminatorKind::Drop { ref mut target, .. } + | TerminatorKind::DropAndReplace { ref mut target, .. } + | TerminatorKind::FalseEdge { real_target: ref mut target, .. } + | TerminatorKind::FalseUnwind { real_target: ref mut target, .. } + | TerminatorKind::Goto { ref mut target } + | TerminatorKind::InlineAsm { destination: Some(ref mut target), .. } + | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block, + ref invalid => bug!("Invalid from_block: {:?}", invalid), + } + } + + fn add_block_from( + &mut self, + some_from_block: Option, + to_kind: TerminatorKind<'tcx>, + ) -> BasicBlock { + let new_block = self.push(1, to_kind); + if let Some(from_block) = some_from_block { + self.link(from_block, new_block); + } + new_block + } + + fn set_branch(&mut self, switchint: BasicBlock, branch_index: usize, to_block: BasicBlock) { + match self.blocks[switchint].terminator_mut().kind { + TerminatorKind::SwitchInt { ref mut targets, .. } => { + let mut branches = targets.iter().collect::>(); + let otherwise = if branch_index == branches.len() { + to_block + } else { + let old_otherwise = targets.otherwise(); + if branch_index > branches.len() { + branches.push((branches.len() as u128, old_otherwise)); + while branches.len() < branch_index { + branches.push((branches.len() as u128, START_BLOCK)); + } + to_block + } else { + branches[branch_index] = (branch_index as u128, to_block); + old_otherwise + } + }; + *targets = SwitchTargets::new(branches.into_iter(), otherwise); + } + ref invalid => bug!("Invalid BasicBlock kind or no to_block: {:?}", invalid), + } + } + + fn call(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from( + some_from_block, + TerminatorKind::Call { + func: Operand::Copy(self.dummy_place.clone()), + args: vec![], + destination: Some((self.dummy_place.clone(), START_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ) + } + + fn goto(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from(some_from_block, TerminatorKind::Goto { target: START_BLOCK }) + } + + fn switchint(&mut self, some_from_block: Option) -> BasicBlock { + let move_ = |place: Place<'tcx>| Operand::Move(place); + let discriminant = Place::from(self.new_temp()); + let switchint_kind = TerminatorKind::SwitchInt { + discr: move_(discriminant), + switch_ty: dummy_ty(), + targets: SwitchTargets::static_if(0, START_BLOCK, START_BLOCK), + }; + self.add_block_from(some_from_block, switchint_kind) + } + + fn return_(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from(some_from_block, TerminatorKind::Return) + } + + fn to_body(self) -> Body<'tcx> { + Body::new_cfg_only(self.blocks) + } +} + +fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { + format!( + "{:?}", + mir_body + .basic_blocks() + .iter_enumerated() + .map(|(bb, data)| { + let kind = &data.terminator().kind; + match kind { + TerminatorKind::Assert { target, .. } + | TerminatorKind::Call { destination: Some((_, target)), .. } + | TerminatorKind::Drop { target, .. } + | TerminatorKind::DropAndReplace { target, .. } + | TerminatorKind::FalseEdge { real_target: target, .. } + | TerminatorKind::FalseUnwind { real_target: target, .. } + | TerminatorKind::Goto { target } + | TerminatorKind::InlineAsm { destination: Some(target), .. } + | TerminatorKind::Yield { resume: target, .. } => { + format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), target) + } + TerminatorKind::SwitchInt { targets, .. } => { + format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), targets) + } + _ => format!("{:?}:{}", bb, debug::term_type(kind)), + } + }) + .collect::>() + ) +} + +static PRINT_GRAPHS: bool = false; + +fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { + if PRINT_GRAPHS { + println!( + "digraph {} {{\n{}\n}}", + name, + mir_body + .basic_blocks() + .iter_enumerated() + .map(|(bb, data)| { + format!( + " {:?} [label=\"{:?}: {}\"];\n{}", + bb, + bb, + debug::term_type(&data.terminator().kind), + mir_body + .successors(bb) + .map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) + .collect::>() + .join("\n") + ) + }) + .collect::>() + .join("\n") + ); + } +} + +fn print_coverage_graphviz( + name: &str, + mir_body: &Body<'_>, + basic_coverage_blocks: &graph::CoverageGraph, +) { + if PRINT_GRAPHS { + println!( + "digraph {} {{\n{}\n}}", + name, + basic_coverage_blocks + .iter_enumerated() + .map(|(bcb, bcb_data)| { + format!( + " {:?} [label=\"{:?}: {}\"];\n{}", + bcb, + bcb, + debug::term_type(&bcb_data.terminator(mir_body).kind), + basic_coverage_blocks + .successors(bcb) + .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) + .collect::>() + .join("\n") + ) + }) + .collect::>() + .join("\n") + ); + } +} + +/// Create a mock `Body` with a simple flow. +fn mir_goto_switchint() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let goto = blocks.goto(Some(start)); + let switchint = blocks.switchint(Some(goto)); + let then_call = blocks.call(None); + let else_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + blocks.set_branch(switchint, 1, else_call); + blocks.return_(Some(then_call)); + blocks.return_(Some(else_call)); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_goto_switchint", &mir_body); + /* Graphviz character plots created using: `graph-easy --as=boxart`: + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌────────────────┐ + │ bb1: Goto │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb4: Call │ ◀── │ bb2: SwitchInt │ + └─────────────┘ └────────────────┘ + │ │ + │ │ + ▼ ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb6: Return │ │ bb3: Call │ + └─────────────┘ └────────────────┘ + │ + │ + ▼ + ┌────────────────┐ + │ bb5: Return │ + └────────────────┘ + */ + mir_body +} + +fn covgraph_goto_switchint() -> graph::CoverageGraph { + let mir_body = mir_goto_switchint(); + if false { + println!("basic_blocks = {}", debug_basic_blocks(&mir_body)); + } + let covgraph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &covgraph); + /* + ┌──────────────┐ ┌─────────────────┐ + │ bcb2: Return │ ◀── │ bcb0: SwitchInt │ + └──────────────┘ └─────────────────┘ + │ + │ + ▼ + ┌─────────────────┐ + │ bcb1: Return │ + └─────────────────┘ + */ + covgraph +} + +/// Create a mock `Body` with a loop. +fn mir_switchint_then_loop_else_return() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let switchint = blocks.switchint(Some(start)); + let then_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + let backedge_goto = blocks.goto(Some(then_call)); + blocks.link(backedge_goto, switchint); + let else_return = blocks.return_(None); + blocks.set_branch(switchint, 1, else_return); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body); + /* + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐ + └─────────────┘ └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb2: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb3: Goto │ ─┘ + └────────────────┘ + */ + mir_body +} + +fn covgraph_switchint_then_loop_else_return() -> graph::CoverageGraph { + let mir_body = mir_switchint_then_loop_else_return(); + let covgraph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_switchint_then_loop_else_return", &mir_body, &covgraph); + /* + ┌─────────────────┐ + │ bcb0: Call │ + └─────────────────┘ + │ + │ + ▼ + ┌────────────┐ ┌─────────────────┐ + │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐ + └────────────┘ └─────────────────┘ │ + │ │ │ + │ │ │ + │ ▼ │ + │ ┌─────────────────┐ │ + │ │ bcb2: Return │ │ + │ └─────────────────┘ │ + │ │ + └─────────────────────────────────────┘ + */ + covgraph +} + +/// Create a mock `Body` with nested loops. +fn mir_switchint_loop_then_inner_loop_else_break() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let switchint = blocks.switchint(Some(start)); + let then_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + let else_return = blocks.return_(None); + blocks.set_branch(switchint, 1, else_return); + + let inner_start = blocks.call(Some(then_call)); + let inner_switchint = blocks.switchint(Some(inner_start)); + let inner_then_call = blocks.call(None); + blocks.set_branch(inner_switchint, 0, inner_then_call); + let inner_backedge_goto = blocks.goto(Some(inner_then_call)); + blocks.link(inner_backedge_goto, inner_switchint); + let inner_else_break_goto = blocks.goto(None); + blocks.set_branch(inner_switchint, 1, inner_else_break_goto); + + let backedge_goto = blocks.goto(Some(inner_else_break_goto)); + blocks.link(backedge_goto, switchint); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body); + /* + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐ + └─────────────┘ └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb2: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb4: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌─────────────┐ ┌────────────────┐ │ + │ bb8: Goto │ ◀── │ bb5: SwitchInt │ ◀┐ │ + └─────────────┘ └────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + ▼ ▼ │ │ + ┌─────────────┐ ┌────────────────┐ │ │ + │ bb9: Goto │ ─┐ │ bb6: Call │ │ │ + └─────────────┘ │ └────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────────────┐ │ │ + │ │ bb7: Goto │ ─┘ │ + │ └────────────────┘ │ + │ │ + └───────────────────────────┘ + */ + mir_body +} + +fn covgraph_switchint_loop_then_inner_loop_else_break() -> graph::CoverageGraph { + let mir_body = mir_switchint_loop_then_inner_loop_else_break(); + let covgraph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz( + "covgraph_switchint_loop_then_inner_loop_else_break", + &mir_body, + &covgraph, + ); + /* + ┌─────────────────┐ + │ bcb0: Call │ + └─────────────────┘ + │ + │ + ▼ + ┌──────────────┐ ┌─────────────────┐ + │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐ + └──────────────┘ └─────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌─────────────────┐ │ + │ bcb3: Call │ │ + └─────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌──────────────┐ ┌─────────────────┐ │ + │ bcb6: Goto │ ◀── │ bcb4: SwitchInt │ ◀┼────┐ + └──────────────┘ └─────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌─────────────────┐ │ │ + │ │ bcb5: Goto │ ─┘ │ + │ └─────────────────┘ │ + │ │ + └────────────────────────────────────────────┘ + */ + covgraph +} + +macro_rules! assert_successors { + ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { + let mut successors = $basic_coverage_blocks.successors[$i].clone(); + successors.sort_unstable(); + assert_eq!(successors, vec![$($successor),*]); + } +} + +#[test] +fn test_covgraph_goto_switchint() { + let basic_coverage_blocks = covgraph_goto_switchint(); + assert_eq!( + basic_coverage_blocks.num_nodes(), + 3, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); + assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb2, []); +} + +#[test] +fn test_find_loop_backedges_none() { + let basic_coverage_blocks = covgraph_goto_switchint(); + if false { + println!( + "basic_coverage_blocks = {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + println!("successors = {:?}", basic_coverage_blocks.successors); + } + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 0, + "backedges: {:?}", + backedges + ); +} + +#[test] +fn test_covgraph_switchint_then_loop_else_return() { + let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); + assert_eq!( + basic_coverage_blocks.num_nodes(), + 4, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); +} + +#[test] +fn test_find_loop_backedges_one() { + let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 1, + "backedges: {:?}", + backedges + ); + + let_bcb!(1); + let_bcb!(3); + + assert_eq!(backedges[bcb1], vec![bcb3]); +} + +#[test] +fn test_covgraph_switchint_loop_then_inner_loop_else_break() { + let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + assert_eq!( + basic_coverage_blocks.num_nodes(), + 7, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); + assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); + assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); +} + +#[test] +fn test_find_loop_backedges_two() { + let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 2, + "backedges: {:?}", + backedges + ); + + let_bcb!(1); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); + + assert_eq!(backedges[bcb1], vec![bcb5]); + assert_eq!(backedges[bcb4], vec![bcb6]); +} + +#[test] +fn test_traverse_coverage_with_loops() { + let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let mut traversed_in_order = Vec::new(); + let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks); + while let Some(bcb) = traversal.next(&basic_coverage_blocks) { + traversed_in_order.push(bcb); + } + + let_bcb!(6); + + // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except* + // bcb6 are inside the first loop. + assert_eq!( + *traversed_in_order.last().expect("should have elements"), + bcb6, + "bcb6 should not be visited until all nodes inside the first loop have been visited" + ); +} From ecfeac58aae332b5f88388624d9656ec6750e011 Mon Sep 17 00:00:00 2001 From: Poliorcetics Date: Thu, 12 Nov 2020 01:55:28 +0100 Subject: [PATCH 21/39] Use intradoc-links for the whole test, add a @has check Co-authored-by: Joshua Nelson --- src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index d758d9936132f..0c047e6249a53 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,12 +1,13 @@ // ignore-tidy-linelength pub mod internal { + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.mod.html' pub struct r#mod; /// See [name], [other name] /// /// [name]: mod - /// [other name]: ../internal/struct.mod.html + /// [other name]: crate::internal::mod // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' pub struct B; From 562d50eb7b67109ca420eab8705673b138863e6a Mon Sep 17 00:00:00 2001 From: Zachary Catlin Date: Wed, 11 Nov 2020 21:02:21 -0500 Subject: [PATCH 22/39] Include llvm-as in llvm-tools-preview component Including llvm-as adds the ability to include assembly language fragments that can be inlined using LTO. --- src/bootstrap/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3d111839dc725..ad8b9c7253c68 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -177,6 +177,7 @@ const LLVM_TOOLS: &[&str] = &[ "llvm-size", // used to prints the size of the linker sections of a program "llvm-strip", // used to discard symbols from binary files to reduce their size "llvm-ar", // used for creating and modifying archive files + "llvm-as", // used to convert LLVM assembly to LLVM bitcode "llvm-dis", // used to disassemble LLVM bitcode "llc", // used to compile LLVM bytecode "opt", // used to optimize LLVM bytecode From eb9f2bb3b0a3f7e712efa28743acf6134d49de5c Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 11 Nov 2020 20:01:10 -0800 Subject: [PATCH 23/39] Overcome Sync issues with non-parallel compiler Per Mark's recommendation at: https://github.com/rust-lang/rust/pull/78963#issuecomment-725790071 --- compiler/rustc_middle/src/ty/mod.rs | 12 ++++++++++++ .../rustc_mir/src/transform/coverage/tests.rs | 19 ++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0042b4a3a4279..d4f299b3f37b9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -611,6 +611,18 @@ pub struct TyS<'tcx> { outer_exclusive_binder: ty::DebruijnIndex, } +impl<'tcx> TyS<'tcx> { + /// A constructor used only for internal testing. + #[allow(rustc::usage_of_ty_tykind)] + pub fn make_for_test( + kind: TyKind<'tcx>, + flags: TypeFlags, + outer_exclusive_binder: ty::DebruijnIndex, + ) -> TyS<'tcx> { + TyS { kind, flags, outer_exclusive_binder } + } +} + // `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] static_assert_size!(TyS<'_>, 32); diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 2231fe6427fa2..3e305a58c6bb2 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -7,18 +7,19 @@ use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyS}; +use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; use rustc_span::DUMMY_SP; -use std::lazy::SyncOnceCell; - -fn dummy_ty<'tcx>() -> &'static TyS<'tcx> { - static DUMMY_TYS: SyncOnceCell> = SyncOnceCell::new(); +fn dummy_ty() -> &'static TyS<'static> { + thread_local! { + static DUMMY_TYS: &'static TyS<'static> = Box::leak(box TyS::make_for_test( + ty::Bool, + TypeFlags::empty(), + DebruijnIndex::from_usize(0), + )); + } - &DUMMY_TYS.get_or_init(|| { - let fake_type_bytes = vec![0 as u8; std::mem::size_of::>()]; - unsafe { std::ptr::read_unaligned::>(fake_type_bytes.as_ptr() as *const TyS<'_>) } - }) + &DUMMY_TYS.with(|tys| *tys) } struct MockBlocks<'tcx> { From 775f1e5acdeae38eae7de41b22bdf6ab71e34efd Mon Sep 17 00:00:00 2001 From: thiolliere Date: Thu, 12 Nov 2020 12:41:19 +0100 Subject: [PATCH 24/39] fix pretty print for qpath --- compiler/rustc_ast_pretty/src/pprust/state.rs | 11 ++++++----- src/test/pretty/qpath-associated-type-bound.rs | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 src/test/pretty/qpath-associated-type-bound.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a64014f5acbb8..62a9452c9f0aa 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2328,11 +2328,12 @@ impl<'a> State<'a> { self.print_path(path, false, depth); } self.s.word(">"); - self.s.word("::"); - let item_segment = path.segments.last().unwrap(); - self.print_ident(item_segment.ident); - if let Some(ref args) = item_segment.args { - self.print_generic_args(args, colons_before_params) + for item_segment in &path.segments[qself.position..] { + self.s.word("::"); + self.print_ident(item_segment.ident); + if let Some(ref args) = item_segment.args { + self.print_generic_args(args, colons_before_params) + } } } diff --git a/src/test/pretty/qpath-associated-type-bound.rs b/src/test/pretty/qpath-associated-type-bound.rs new file mode 100644 index 0000000000000..e06885e03882b --- /dev/null +++ b/src/test/pretty/qpath-associated-type-bound.rs @@ -0,0 +1,16 @@ +// pp-exact + + +mod m { + pub trait Tr { + type Ts: super::Tu; + } +} + +trait Tu { + fn dummy() { } +} + +fn foo() { ::Ts::dummy(); } + +fn main() { } From 04d41e1f4083307b1434d305899da739b21e5155 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 12 Nov 2020 00:54:23 +0300 Subject: [PATCH 25/39] rustc_target: Mark UEFI targets as `is_like_windows`/`is_like_msvc` Document what `is_like_windows` and `is_like_msvc` mean in more detail. --- compiler/rustc_codegen_llvm/src/back/write.rs | 4 +--- compiler/rustc_codegen_ssa/src/back/write.rs | 2 ++ compiler/rustc_target/src/spec/mod.rs | 19 ++++++++++++++++--- .../rustc_target/src/spec/tests/tests_impl.rs | 2 ++ .../rustc_target/src/spec/uefi_msvc_base.rs | 9 --------- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index e6acb6860be9e..6237a4c0020eb 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -925,9 +925,7 @@ unsafe fn embed_bitcode( || cgcx.opts.target_triple.triple().starts_with("asmjs") { // nothing to do here - } else if cgcx.opts.target_triple.triple().contains("windows") - || cgcx.opts.target_triple.triple().contains("uefi") - { + } else if cgcx.is_pe_coff { let asm = " .section .llvmbc,\"n\" .section .llvmcmd,\"n\" diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index b34bee3358b40..7f2bb7b5bcdaf 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -307,6 +307,7 @@ pub struct CodegenContext { pub allocator_module_config: Arc, pub tm_factory: TargetMachineFactory, pub msvc_imps_needed: bool, + pub is_pe_coff: bool, pub target_pointer_width: u32, pub target_arch: String, pub debuginfo: config::DebugInfo, @@ -1022,6 +1023,7 @@ fn start_executing_work( tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), + is_pe_coff: tcx.sess.target.is_like_windows, target_pointer_width: tcx.sess.target.pointer_width, target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f949bf95a502a..cf45d8b7f6abd 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -819,10 +819,23 @@ pub struct TargetOptions { /// Only useful for compiling against Illumos/Solaris, /// as they have a different set of linker flags. Defaults to false. pub is_like_solaris: bool, - /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows, - /// only really used for figuring out how to find libraries, since Windows uses its own - /// library naming convention. Defaults to false. + /// Whether the target is like Windows. + /// This is a combination of several more specific properties represented as a single flag: + /// - The target uses a Windows ABI, + /// - uses PE/COFF as a format for object code, + /// - uses Windows-style dllexport/dllimport for shared libraries, + /// - uses import libraries and .def files for symbol exports, + /// - executables support setting a subsystem. pub is_like_windows: bool, + /// Whether the target is like MSVC. + /// This is a combination of several more specific properties represented as a single flag: + /// - The target has all the properties from `is_like_windows` + /// (for in-tree targets "is_like_msvc ⇒ is_like_windows" is ensured by a unit test), + /// - has some MSVC-specific Windows ABI properties, + /// - uses a link.exe-like linker, + /// - uses CodeView/PDB for debuginfo and natvis for its visualization, + /// - uses SEH-based unwinding, + /// - supports control flow guard mechanism. pub is_like_msvc: bool, /// Whether the target toolchain is like Emscripten's. Only useful for compiling with /// Emscripten toolchain. diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index f348df7d5a716..10e62f6b672e3 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -8,6 +8,7 @@ pub(super) fn test_target(target: Target) { impl Target { fn check_consistency(&self) { + assert!(self.is_like_windows || !self.is_like_msvc); // Check that LLD with the given flavor is treated identically to the linker it emulates. // If your target really needs to deviate from the rules below, except it and document the // reasons. @@ -16,6 +17,7 @@ impl Target { || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link), self.lld_flavor == LldFlavor::Link, ); + assert_eq!(self.is_like_msvc, self.lld_flavor == LldFlavor::Link); for args in &[ &self.pre_link_args, &self.late_link_args, diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 79fe77495e731..322b6f530e9fd 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -46,15 +46,6 @@ pub fn opts() -> TargetOptions { stack_probes: true, singlethread: true, linker: Some("rust-lld".to_string()), - // FIXME: This should likely be `true` inherited from `msvc_base` - // because UEFI follows Windows ABI and uses PE/COFF. - // The `false` is probably causing ABI bugs right now. - is_like_windows: false, - // FIXME: This should likely be `true` inherited from `msvc_base` - // because UEFI follows Windows ABI and uses PE/COFF. - // The `false` is probably causing ABI bugs right now. - is_like_msvc: false, - ..base } } From 0b4af1614d91152564b852572afa63fab71162a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 26/39] Never inline when `no_sanitize` attributes differ The inliner looks if a sanitizer is enabled before considering `no_sanitize` attribute as possible source of incompatibility. The MIR inlining could happen in a crate with sanitizer disabled, but code generation in a crate with sanitizer enabled, thus the attribute would be incorrectly ignored. To avoid the issue never inline functions with different `no_sanitize` attributes. --- compiler/rustc_mir/src/transform/inline.rs | 6 +----- src/test/mir-opt/inline/inline-compatibility.rs | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 7737672dbde66..7332d4194944e 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -218,11 +218,7 @@ impl Inliner<'tcx> { return false; } - let self_no_sanitize = - self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; - let callee_no_sanitize = - codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; - if self_no_sanitize != callee_no_sanitize { + if self.codegen_fn_attrs.no_sanitize != codegen_fn_attrs.no_sanitize { debug!("`callee has incompatible no_sanitize attribute - not inlining"); return false; } diff --git a/src/test/mir-opt/inline/inline-compatibility.rs b/src/test/mir-opt/inline/inline-compatibility.rs index ff9049edb4f2c..2e9edf5260f53 100644 --- a/src/test/mir-opt/inline/inline-compatibility.rs +++ b/src/test/mir-opt/inline/inline-compatibility.rs @@ -1,8 +1,6 @@ // Checks that only functions with compatible attributes are inlined. // // only-x86_64 -// needs-sanitizer-address -// compile-flags: -Zsanitizer=address #![crate_type = "lib"] #![feature(no_sanitize)] @@ -35,5 +33,5 @@ pub unsafe fn not_inlined_no_sanitize() { pub unsafe fn target_feature() {} #[inline] -#[no_sanitize(address, memory)] +#[no_sanitize(address)] pub unsafe fn no_sanitize() {} From ae4332643de6a672ab3aefd62a3063c9af21166c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 27/39] Never inline cold functions The information about cold attribute is lost during inlining, Avoid the issue by never inlining cold functions. --- compiler/rustc_mir/src/transform/inline.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 7332d4194944e..5023e49df3abb 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -252,9 +252,9 @@ impl Inliner<'tcx> { self.tcx.sess.opts.debugging_opts.inline_mir_threshold }; - // Significantly lower the threshold for inlining cold functions if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { - threshold /= 5; + debug!("#[cold] present - not inlining"); + return false; } // Give a bonus functions with a small number of blocks, From 9bb3d6b7d472e2116312ea45db07a5338af205fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 28/39] Remove check for impossible condition The callee body is already transformed; the condition is always false. --- compiler/rustc_mir/src/transform/inline.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 5023e49df3abb..0d6d9e397ac9f 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -203,12 +203,6 @@ impl Inliner<'tcx> { debug!("should_inline({:?})", callsite); let tcx = self.tcx; - // Cannot inline generators which haven't been transformed yet - if callee_body.yield_ty.is_some() { - debug!(" yield ty present - not inlining"); - return false; - } - let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); let self_features = &self.codegen_fn_attrs.target_features; From 2879ab793e10a1bf5c158e3301474be96192aa7a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 4 Nov 2020 16:27:11 +0300 Subject: [PATCH 29/39] rustc_parse: Remove optimization for 0-length streams in `collect_tokens` The optimization conflates empty token streams with unknown token stream, which is at least suspicious, and doesn't affect performance because 0-length token streams are very rare. --- compiler/rustc_parse/src/parser/mod.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index da1c54e88b5e2..40aa2db58c720 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1180,8 +1180,7 @@ impl<'a> Parser<'a> { /// Records all tokens consumed by the provided callback, /// including the current token. These tokens are collected /// into a `LazyTokenStream`, and returned along with the result - /// of the callback. The returned `LazyTokenStream` will be `None` - /// if not tokens were captured. + /// of the callback. /// /// Note: If your callback consumes an opening delimiter /// (including the case where you call `collect_tokens` @@ -1203,17 +1202,14 @@ impl<'a> Parser<'a> { let ret = f(self)?; - // We didn't capture any tokens - let num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; - if num_calls == 0 { - return Ok((ret, None)); - } - // Produces a `TokenStream` on-demand. Using `cursor_snapshot` // and `num_calls`, we can reconstruct the `TokenStream` seen // by the callback. This allows us to avoid producing a `TokenStream` // if it is never needed - for example, a captured `macro_rules!` // argument that is never passed to a proc macro. + // In practice token stream creation happens rarely compared to + // calls to `collect_tokens` (see some statistics in #78736), + // so we are doing as little up-front work as possible. // // This also makes `Parser` very cheap to clone, since // there is no intermediate collection buffer to clone. @@ -1247,8 +1243,8 @@ impl<'a> Parser<'a> { let lazy_impl = LazyTokenStreamImpl { start_token, + num_calls: self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls, cursor_snapshot, - num_calls, desugar_doc_comments: self.desugar_doc_comments, }; Ok((ret, Some(LazyTokenStream::new(lazy_impl)))) From 66cadec1763ac645337c1ac58f06ea48b9b72a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 30/39] Fix generator inlining by checking for rust-call abi and spread arg --- compiler/rustc_mir/src/transform/inline.rs | 26 +++++++++++---------- src/test/mir-opt/inline/inline-generator.rs | 16 +++++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/test/mir-opt/inline/inline-generator.rs diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 0d6d9e397ac9f..2ccb9b3709f2f 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -7,6 +7,7 @@ use rustc_index::vec::Idx; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_span::{hygiene::ExpnKind, ExpnData, Span}; use rustc_target::spec::abi::Abi; @@ -28,6 +29,7 @@ pub struct Inline; #[derive(Copy, Clone, Debug)] struct CallSite<'tcx> { callee: Instance<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, block: BasicBlock, target: Option, source_info: SourceInfo, @@ -173,22 +175,23 @@ impl Inliner<'tcx> { // Only consider direct calls to functions let terminator = bb_data.terminator(); - if let TerminatorKind::Call { func: ref op, ref destination, .. } = terminator.kind { - if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { - // To resolve an instance its substs have to be fully normalized, so - // we do this here. - let normalized_substs = self.tcx.normalize_erasing_regions(self.param_env, substs); + if let TerminatorKind::Call { ref func, ref destination, .. } = terminator.kind { + let func_ty = func.ty(caller_body, self.tcx); + if let ty::FnDef(def_id, substs) = *func_ty.kind() { + // To resolve an instance its substs have to be fully normalized. + let substs = self.tcx.normalize_erasing_regions(self.param_env, substs); let callee = - Instance::resolve(self.tcx, self.param_env, callee_def_id, normalized_substs) - .ok() - .flatten()?; + Instance::resolve(self.tcx, self.param_env, def_id, substs).ok().flatten()?; if let InstanceDef::Virtual(..) | InstanceDef::Intrinsic(_) = callee.def { return None; } + let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs); + return Some(CallSite { callee, + fn_sig, block: bb, target: destination.map(|(_, target)| target), source_info: terminator.source_info, @@ -437,7 +440,7 @@ impl Inliner<'tcx> { }; // Copy the arguments if needed. - let args: Vec<_> = self.make_call_args(args, &callsite, caller_body); + let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body); let mut integrator = Integrator { args: &args, @@ -518,6 +521,7 @@ impl Inliner<'tcx> { args: Vec>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, + callee_body: &Body<'tcx>, ) -> Vec { let tcx = self.tcx; @@ -544,9 +548,7 @@ impl Inliner<'tcx> { // tmp2 = tuple_tmp.2 // // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. - // FIXME(eddyb) make this check for `"rust-call"` ABI combined with - // `callee_body.spread_arg == None`, instead of special-casing closures. - if tcx.is_closure(callsite.callee.def_id()) { + if callsite.fn_sig.abi() == Abi::RustCall && callee_body.spread_arg.is_none() { let mut args = args.into_iter(); let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); diff --git a/src/test/mir-opt/inline/inline-generator.rs b/src/test/mir-opt/inline/inline-generator.rs new file mode 100644 index 0000000000000..d11b3e548f721 --- /dev/null +++ b/src/test/mir-opt/inline/inline-generator.rs @@ -0,0 +1,16 @@ +// ignore-wasm32-bare compiled with panic=abort by default +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::pin::Pin; + +// EMIT_MIR inline_generator.main.Inline.diff +fn main() { + let _r = Pin::new(&mut g()).resume(false); +} + +#[inline(always)] +pub fn g() -> impl Generator { + #[inline(always)] + |a| { yield if a { 7 } else { 13 } } +} From 79d853eccebf6d9704adeea45f20f11bae0d7396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 31/39] Never inline C variadic functions --- compiler/rustc_mir/src/transform/inline.rs | 5 +++++ .../mir-opt/inline/inline-compatibility.rs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 2ccb9b3709f2f..aae98f5b6d8d6 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -206,6 +206,11 @@ impl Inliner<'tcx> { debug!("should_inline({:?})", callsite); let tcx = self.tcx; + if callsite.fn_sig.c_variadic() { + debug!("callee is variadic - not inlining"); + return false; + } + let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); let self_features = &self.codegen_fn_attrs.target_features; diff --git a/src/test/mir-opt/inline/inline-compatibility.rs b/src/test/mir-opt/inline/inline-compatibility.rs index 2e9edf5260f53..30aff0a64efb9 100644 --- a/src/test/mir-opt/inline/inline-compatibility.rs +++ b/src/test/mir-opt/inline/inline-compatibility.rs @@ -5,6 +5,7 @@ #![crate_type = "lib"] #![feature(no_sanitize)] #![feature(target_feature_11)] +#![feature(c_variadic)] // EMIT_MIR inline_compatibility.inlined_target_feature.Inline.diff #[target_feature(enable = "sse2")] @@ -35,3 +36,20 @@ pub unsafe fn target_feature() {} #[inline] #[no_sanitize(address)] pub unsafe fn no_sanitize() {} + +// EMIT_MIR inline_compatibility.not_inlined_c_variadic.Inline.diff +pub unsafe fn not_inlined_c_variadic() { + let s = sum(4u32, 4u32, 30u32, 200u32, 1000u32); +} + +#[no_mangle] +#[inline(always)] +unsafe extern "C" fn sum(n: u32, mut vs: ...) -> u32 { + let mut s = 0; + let mut i = 0; + while i != n { + s += vs.arg::(); + i += 1; + } + s +} From 2a010dd3404256ea774e5338ad1952d09ab2cf03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 32/39] ./x.py test --bless --- ...patibility.inlined_no_sanitize.Inline.diff | 20 +-- ...ibility.inlined_target_feature.Inline.diff | 20 +-- ...ibility.not_inlined_c_variadic.Inline.diff | 25 ++++ ...bility.not_inlined_no_sanitize.Inline.diff | 16 +-- ...ity.not_inlined_target_feature.Inline.diff | 16 +-- .../inline/inline_generator.main.Inline.diff | 123 ++++++++++++++++++ 6 files changed, 184 insertions(+), 36 deletions(-) create mode 100644 src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_generator.main.Inline.diff diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff index 451ec39422fc4..c95cf47695785 100644 --- a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff @@ -2,24 +2,24 @@ + // MIR for `inlined_no_sanitize` after Inline fn inlined_no_sanitize() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:24:37: 24:37 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 -+ scope 1 (inlined no_sanitize) { // at $DIR/inline-compatibility.rs:25:5: 25:18 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:23:37: 23:37 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 ++ scope 1 (inlined no_sanitize) { // at $DIR/inline-compatibility.rs:24:5: 24:18 + } bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 -- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 +- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 - // mir::Constant -- // + span: $DIR/inline-compatibility.rs:25:5: 25:16 +- // + span: $DIR/inline-compatibility.rs:24:5: 24:16 - // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } - } - - bb1: { -+ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:25:5: 25:18 - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:25:18: 25:19 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:24:37: 26:2 - return; // scope 0 at $DIR/inline-compatibility.rs:26:2: 26:2 ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:24:5: 24:18 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:24:18: 24:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:23:37: 25:2 + return; // scope 0 at $DIR/inline-compatibility.rs:25:2: 25:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff index a59ddd344cb26..2bb928343229f 100644 --- a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff @@ -2,24 +2,24 @@ + // MIR for `inlined_target_feature` after Inline fn inlined_target_feature() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:13:40: 13:40 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 -+ scope 1 (inlined target_feature) { // at $DIR/inline-compatibility.rs:14:5: 14:21 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:12:40: 12:40 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 ++ scope 1 (inlined target_feature) { // at $DIR/inline-compatibility.rs:13:5: 13:21 + } bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 -- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 +- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 - // mir::Constant -- // + span: $DIR/inline-compatibility.rs:14:5: 14:19 +- // + span: $DIR/inline-compatibility.rs:13:5: 13:19 - // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } - } - - bb1: { -+ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:14:5: 14:21 - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:14:21: 14:22 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:13:40: 15:2 - return; // scope 0 at $DIR/inline-compatibility.rs:15:2: 15:2 ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:13:5: 13:21 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:13:21: 13:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:12:40: 14:2 + return; // scope 0 at $DIR/inline-compatibility.rs:14:2: 14:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff new file mode 100644 index 0000000000000..09bca903c80e8 --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff @@ -0,0 +1,25 @@ +- // MIR for `not_inlined_c_variadic` before Inline ++ // MIR for `not_inlined_c_variadic` after Inline + + fn not_inlined_c_variadic() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:41:40: 41:40 + let _1: u32; // in scope 0 at $DIR/inline-compatibility.rs:42:9: 42:10 + scope 1 { + debug s => _1; // in scope 1 at $DIR/inline-compatibility.rs:42:9: 42:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:42:9: 42:10 + _1 = sum(const 4_u32, const 4_u32, const 30_u32, const 200_u32, const 1000_u32) -> bb1; // scope 0 at $DIR/inline-compatibility.rs:42:13: 42:52 + // mir::Constant + // + span: $DIR/inline-compatibility.rs:42:13: 42:16 + // + literal: Const { ty: unsafe extern "C" fn(u32, ...) -> u32 {sum}, val: Value(Scalar()) } + } + + bb1: { + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:41:40: 43:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:43:1: 43:2 + return; // scope 0 at $DIR/inline-compatibility.rs:43:2: 43:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff index 651eadc1e849c..5af3946f2e501 100644 --- a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff @@ -2,21 +2,21 @@ + // MIR for `not_inlined_no_sanitize` after Inline fn not_inlined_no_sanitize() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:29:41: 29:41 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:28:41: 28:41 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 - _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 + _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 // mir::Constant - // + span: $DIR/inline-compatibility.rs:30:5: 30:16 + // + span: $DIR/inline-compatibility.rs:29:5: 29:16 // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } } bb1: { - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:30:18: 30:19 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:29:41: 31:2 - return; // scope 0 at $DIR/inline-compatibility.rs:31:2: 31:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:29:18: 29:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:28:41: 30:2 + return; // scope 0 at $DIR/inline-compatibility.rs:30:2: 30:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff index 55b9edf3adc1f..8c9fa573ce218 100644 --- a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff @@ -2,21 +2,21 @@ + // MIR for `not_inlined_target_feature` after Inline fn not_inlined_target_feature() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:18:44: 18:44 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:17:44: 17:44 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 - _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 + _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 // mir::Constant - // + span: $DIR/inline-compatibility.rs:19:5: 19:19 + // + span: $DIR/inline-compatibility.rs:18:5: 18:19 // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } } bb1: { - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:19:21: 19:22 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:18:44: 20:2 - return; // scope 0 at $DIR/inline-compatibility.rs:20:2: 20:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:18:21: 18:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:17:44: 19:2 + return; // scope 0 at $DIR/inline-compatibility.rs:19:2: 19:2 } } diff --git a/src/test/mir-opt/inline/inline_generator.main.Inline.diff b/src/test/mir-opt/inline/inline_generator.main.Inline.diff new file mode 100644 index 0000000000000..aa32daa82dd51 --- /dev/null +++ b/src/test/mir-opt/inline/inline_generator.main.Inline.diff @@ -0,0 +1,123 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-generator.rs:8:11: 8:11 + let _1: std::ops::GeneratorState< as std::ops::Generator>::Yield, as std::ops::Generator>::Return>; // in scope 0 at $DIR/inline-generator.rs:9:9: 9:11 + let mut _2: std::pin::Pin<&mut impl std::ops::Generator>; // in scope 0 at $DIR/inline-generator.rs:9:14: 9:32 + let mut _3: &mut impl std::ops::Generator; // in scope 0 at $DIR/inline-generator.rs:9:23: 9:31 + let mut _4: impl std::ops::Generator; // in scope 0 at $DIR/inline-generator.rs:9:28: 9:31 ++ let mut _7: bool; // in scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + scope 1 { + debug _r => _1; // in scope 1 at $DIR/inline-generator.rs:9:9: 9:11 + } ++ scope 2 (inlined g) { // at $DIR/inline-generator.rs:9:28: 9:31 ++ } ++ scope 3 (inlined Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]>::new) { // at $DIR/inline-generator.rs:9:14: 9:32 ++ debug pointer => _3; // in scope 3 at $DIR/inline-generator.rs:9:14: 9:32 ++ let mut _5: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]; // in scope 3 at $DIR/inline-generator.rs:9:14: 9:32 ++ scope 4 { ++ scope 5 (inlined Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]>::new_unchecked) { // at $DIR/inline-generator.rs:9:14: 9:32 ++ debug pointer => _5; // in scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ let mut _6: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]; // in scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ } ++ } ++ } ++ scope 6 (inlined g::{closure#0}) { // at $DIR/inline-generator.rs:9:14: 9:46 ++ debug a => _8; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ let mut _8: bool; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ let mut _9: u32; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-generator.rs:9:9: 9:11 + StorageLive(_2); // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 + StorageLive(_3); // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 + StorageLive(_4); // scope 0 at $DIR/inline-generator.rs:9:28: 9:31 +- _4 = g() -> bb1; // scope 0 at $DIR/inline-generator.rs:9:28: 9:31 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:28: 9:29 +- // + literal: Const { ty: fn() -> impl std::ops::Generator {g}, val: Value(Scalar()) } +- } +- +- bb1: { ++ discriminant(_4) = 0; // scope 2 at $DIR/inline-generator.rs:9:28: 9:31 + _3 = &mut _4; // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 +- _2 = Pin::<&mut impl Generator>::new(move _3) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:14: 9:22 +- // + user_ty: UserType(0) +- // + literal: Const { ty: fn(&mut impl std::ops::Generator) -> std::pin::Pin<&mut impl std::ops::Generator> {std::pin::Pin::<&mut impl std::ops::Generator>::new}, val: Value(Scalar()) } +- } +- +- bb2: { ++ StorageLive(_5); // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 ++ _5 = move _3; // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageLive(_6); // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ _6 = move _5; // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ (_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]) = move _6; // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageDead(_6); // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageDead(_5); // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 + StorageDead(_3); // scope 0 at $DIR/inline-generator.rs:9:31: 9:32 +- _1 = as Generator>::resume(move _2, const false) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:33: 9:39 +- // + literal: Const { ty: for<'r> fn(std::pin::Pin<&'r mut impl std::ops::Generator>, bool) -> std::ops::GeneratorState< as std::ops::Generator>::Yield, as std::ops::Generator>::Return> { as std::ops::Generator>::resume}, val: Value(Scalar()) } ++ StorageLive(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 ++ _7 = const false; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 ++ _9 = discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))); // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ switchInt(move _9) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 + } + +- bb3: { ++ bb1: { ++ StorageDead(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + StorageDead(_2); // scope 0 at $DIR/inline-generator.rs:9:45: 9:46 + StorageDead(_4); // scope 0 at $DIR/inline-generator.rs:9:46: 9:47 + _0 = const (); // scope 0 at $DIR/inline-generator.rs:8:11: 10:2 + StorageDead(_1); // scope 0 at $DIR/inline-generator.rs:10:1: 10:2 + return; // scope 0 at $DIR/inline-generator.rs:10:2: 10:2 + } + +- bb4 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/inline-generator.rs:8:1: 10:2 ++ } ++ ++ bb3: { ++ _8 = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ switchInt(_8) -> [false: bb4, otherwise: bb5]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb4: { ++ ((_1 as Yielded).0: i32) = const 13_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb6; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb5: { ++ ((_1 as Yielded).0: i32) = const 7_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb6; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb6: { ++ discriminant(_1) = 0; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 3; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb1; // scope 0 at $DIR/inline-generator.rs:15:11: 15:39 ++ } ++ ++ bb7: { ++ ((_1 as Complete).0: bool) = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant(_1) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb1; // scope 0 at $DIR/inline-generator.rs:15:41: 15:41 ++ } ++ ++ bb8: { ++ assert(const false, "generator resumed after completion") -> [success: bb8, unwind: bb2]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb9: { ++ unreachable; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 + } + } + From d486bfcbff107e8a6769e00c59d02b13c664b6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 12 Nov 2020 00:00:00 +0000 Subject: [PATCH 33/39] Normalize function type during validation During inlining, the callee body is normalized and has types revealed, but some of locals corresponding to the arguments might come from the caller body which is not. As a result the caller body does not pass validation without additional normalization. --- compiler/rustc_mir/src/transform/validate.rs | 2 ++ .../auxiliary/lib.rs | 4 ---- .../ui/mir/mir-inlining/ice-issue-77306-1.rs | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index e1e6e71acb5a8..2fbfe13082f89 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -357,7 +357,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Call { func, args, destination, cleanup, .. } => { + let param_env = self.param_env.with_reveal_all_normalized(self.tcx); let func_ty = func.ty(&self.body.local_decls, self.tcx); + let func_ty = self.tcx.normalize_erasing_regions(param_env, func_ty); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( diff --git a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs index fb4bf2b8b44e7..f3a51b415faca 100644 --- a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs +++ b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs @@ -1,7 +1,3 @@ -// revisions: default miropt -//[miropt]compile-flags: -Z mir-opt-level=2 -// ~^ This flag is for #77668, it used to be ICE. - #![crate_type = "lib"] pub fn bar

( // Error won't happen if "bar" is not generic diff --git a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs index 4d083bf232155..ccb279f7fa212 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs @@ -1,17 +1,27 @@ -// run-pass +// Regression test for various issues related to normalization & inlining. +// * #68347, #77306, #77668 - missed normalization during inlining. +// * #78442 - missed normalization in validator after inlining. +// +// build-pass // compile-flags:-Zmir-opt-level=2 -// Previously ICEd because we did not normalize during inlining, -// see https://github.com/rust-lang/rust/pull/77306 for more discussion. - pub fn write() { create()() } +pub fn write_generic(_t: T) { + hide()(); +} + pub fn create() -> impl FnOnce() { || () } +pub fn hide() -> impl Fn() { + write +} + fn main() { write(); + write_generic(()); } From 99be78d135e73197e04221c139a219ea6436e72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 12 Nov 2020 00:00:00 +0000 Subject: [PATCH 34/39] Always use param_env_reveal_all_normalized in validator --- compiler/rustc_mir/src/transform/validate.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index 2fbfe13082f89..876ecee80c6a1 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -38,7 +38,9 @@ pub struct Validator { impl<'tcx> MirPass<'tcx> for Validator { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); - let param_env = tcx.param_env(def_id); + // We need to param_env_reveal_all_normalized, as some optimizations + // change types in ways that require unfolding opaque types. + let param_env = tcx.param_env_reveal_all_normalized(def_id); let mir_phase = self.mir_phase; let always_live_locals = AlwaysLiveLocals::new(body); @@ -79,7 +81,6 @@ pub fn equal_up_to_regions( } // Normalize lifetimes away on both sides, then compare. - let param_env = param_env.with_reveal_all_normalized(tcx); let normalize = |ty: Ty<'tcx>| { tcx.normalize_erasing_regions( param_env, @@ -167,17 +168,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return true; } // Normalize projections and things like that. - // FIXME: We need to reveal_all, as some optimizations change types in ways - // that require unfolding opaque types. - let param_env = self.param_env.with_reveal_all_normalized(self.tcx); - let src = self.tcx.normalize_erasing_regions(param_env, src); - let dest = self.tcx.normalize_erasing_regions(param_env, dest); + let src = self.tcx.normalize_erasing_regions(self.param_env, src); + let dest = self.tcx.normalize_erasing_regions(self.param_env, dest); // Type-changing assignments can happen when subtyping is used. While // all normal lifetimes are erased, higher-ranked types with their // late-bound lifetimes are still around and can lead to type // differences. So we compare ignoring lifetimes. - equal_up_to_regions(self.tcx, param_env, src, dest) + equal_up_to_regions(self.tcx, self.param_env, src, dest) } } @@ -357,9 +355,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Call { func, args, destination, cleanup, .. } => { - let param_env = self.param_env.with_reveal_all_normalized(self.tcx); let func_ty = func.ty(&self.body.local_decls, self.tcx); - let func_ty = self.tcx.normalize_erasing_regions(param_env, func_ty); + let func_ty = self.tcx.normalize_erasing_regions(self.param_env, func_ty); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( From c131063988ff8ac86bbf53a3bf998145c6b99d6f Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 12 Nov 2020 16:08:42 -0800 Subject: [PATCH 35/39] Added a unit test for BcbCounters Restructured the code a little, to allow getting both the mir::Body and coverage graph. --- .../src/transform/coverage/counters.rs | 1 - .../rustc_mir/src/transform/coverage/spans.rs | 10 +- .../rustc_mir/src/transform/coverage/tests.rs | 279 +++++++++++------- 3 files changed, 188 insertions(+), 102 deletions(-) diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index 7c4b30ca9e790..20f6a16e0f757 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -120,7 +120,6 @@ struct BcbCounters<'a> { basic_coverage_blocks: &'a mut CoverageGraph, } -// FIXME(richkadel): Add unit tests for `BcbCounters` functions/algorithms. impl<'a> BcbCounters<'a> { fn new( coverage_counters: &'a mut CoverageCounters, diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index f880d69bd64f6..95c49922262f6 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -645,7 +645,10 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { } } -fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { +pub(super) fn filtered_statement_span( + statement: &'a Statement<'tcx>, + body_span: Span, +) -> Option { match statement.kind { // These statements have spans that are often outside the scope of the executed source code // for their parent `BasicBlock`. @@ -686,7 +689,10 @@ fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> O } } -fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { +pub(super) fn filtered_terminator_span( + terminator: &'a Terminator<'tcx>, + body_span: Span, +) -> Option { match terminator.kind { // These terminators have spans that don't positively contribute to computing a reasonable // span of actually executed source code. (For example, SwitchInt terminators extracted from diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 3e305a58c6bb2..7de965be53bde 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -1,14 +1,39 @@ +//! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR +//! pass. +//! +//! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage` +//! functions and algorithms. Mocked objects include instances of `mir::Body`; including +//! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on +//! real, runtime versions of these mocked-up objects have constraints (such as cross-thread +//! limitations) and deep dependencies on other elements of the full Rust compiler (which is +//! *not* constructed or mocked for these tests). +//! +//! Of particular note, attempting to simply print elements of the `mir::Body` with default +//! `Debug` formatting can fail because some `Debug` format implementations require the +//! `TyCtxt`, obtained via a static global variable that is *not* set for these tests. +//! Initializing the global type context is prohibitively complex for the scope and scale of these +//! tests (essentially requiring initializing the entire compiler). +//! +//! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which +//! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some +//! basic, coverage-specific features would be impossible to test, but thankfully initializing these +//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument +//! to: `rustc_span::with_default_session_globals(|| { test_here(); })`. + +use super::counters; use super::debug; use super::graph; +use super::spans; use coverage_test_macros::let_bcb; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::*; use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; -use rustc_span::DUMMY_SP; +use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; fn dummy_ty() -> &'static TyS<'static> { thread_local! { @@ -24,7 +49,6 @@ fn dummy_ty() -> &'static TyS<'static> { struct MockBlocks<'tcx> { blocks: IndexVec>, - source_info: SourceInfo, dummy_place: Place<'tcx>, next_local: usize, } @@ -33,7 +57,6 @@ impl<'tcx> MockBlocks<'tcx> { fn new() -> Self { Self { blocks: IndexVec::new(), - source_info: SourceInfo::outermost(DUMMY_SP), dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() }, next_local: 0, } @@ -45,12 +68,19 @@ impl<'tcx> MockBlocks<'tcx> { Local::new(index) } - fn push(&mut self, num_nops: usize, kind: TerminatorKind<'tcx>) -> BasicBlock { - let nop = Statement { source_info: self.source_info, kind: StatementKind::Nop }; - + fn push(&mut self, kind: TerminatorKind<'tcx>) -> BasicBlock { + let next_lo = if let Some(last) = self.blocks.last() { + self.blocks[last].terminator().source_info.span.hi() + } else { + BytePos(1) + }; + let next_hi = next_lo + BytePos(1); self.blocks.push(BasicBlockData { - statements: std::iter::repeat(&nop).cloned().take(num_nops).collect(), - terminator: Some(Terminator { source_info: self.source_info, kind }), + statements: vec![], + terminator: Some(Terminator { + source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), + kind, + }), is_cleanup: false, }) } @@ -75,7 +105,7 @@ impl<'tcx> MockBlocks<'tcx> { some_from_block: Option, to_kind: TerminatorKind<'tcx>, ) -> BasicBlock { - let new_block = self.push(1, to_kind); + let new_block = self.push(to_kind); if let Some(from_block) = some_from_block { self.link(from_block, new_block); } @@ -152,7 +182,10 @@ fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { .basic_blocks() .iter_enumerated() .map(|(bb, data)| { - let kind = &data.terminator().kind; + let term = &data.terminator(); + let kind = &term.kind; + let span = term.source_info.span; + let sp = format!("(span:{},{})", span.lo().to_u32(), span.hi().to_u32()); match kind { TerminatorKind::Assert { target, .. } | TerminatorKind::Call { destination: Some((_, target)), .. } @@ -163,12 +196,12 @@ fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { | TerminatorKind::Goto { target } | TerminatorKind::InlineAsm { destination: Some(target), .. } | TerminatorKind::Yield { resume: target, .. } => { - format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), target) + format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), target) } TerminatorKind::SwitchInt { targets, .. } => { - format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), targets) + format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), targets) } - _ => format!("{:?}:{}", bb, debug::term_type(kind)), + _ => format!("{}{:?}:{}", sp, bb, debug::term_type(kind)), } }) .collect::>() @@ -235,7 +268,7 @@ fn print_coverage_graphviz( } /// Create a mock `Body` with a simple flow. -fn mir_goto_switchint() -> Body<'a> { +fn goto_switchint() -> Body<'a> { let mut blocks = MockBlocks::new(); let start = blocks.call(None); let goto = blocks.goto(Some(start)); @@ -281,13 +314,22 @@ fn mir_goto_switchint() -> Body<'a> { mir_body } -fn covgraph_goto_switchint() -> graph::CoverageGraph { - let mir_body = mir_goto_switchint(); +macro_rules! assert_successors { + ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { + let mut successors = $basic_coverage_blocks.successors[$i].clone(); + successors.sort_unstable(); + assert_eq!(successors, vec![$($successor),*]); + } +} + +#[test] +fn test_covgraph_goto_switchint() { + let mir_body = goto_switchint(); if false { println!("basic_blocks = {}", debug_basic_blocks(&mir_body)); } - let covgraph = graph::CoverageGraph::from_mir(&mir_body); - print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &covgraph); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks); /* ┌──────────────┐ ┌─────────────────┐ │ bcb2: Return │ ◀── │ bcb0: SwitchInt │ @@ -299,11 +341,24 @@ fn covgraph_goto_switchint() -> graph::CoverageGraph { │ bcb1: Return │ └─────────────────┘ */ - covgraph + assert_eq!( + basic_coverage_blocks.num_nodes(), + 3, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); + assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb2, []); } /// Create a mock `Body` with a loop. -fn mir_switchint_then_loop_else_return() -> Body<'a> { +fn switchint_then_loop_else_return() -> Body<'a> { let mut blocks = MockBlocks::new(); let start = blocks.call(None); let switchint = blocks.switchint(Some(start)); @@ -342,10 +397,15 @@ fn mir_switchint_then_loop_else_return() -> Body<'a> { mir_body } -fn covgraph_switchint_then_loop_else_return() -> graph::CoverageGraph { - let mir_body = mir_switchint_then_loop_else_return(); - let covgraph = graph::CoverageGraph::from_mir(&mir_body); - print_coverage_graphviz("covgraph_switchint_then_loop_else_return", &mir_body, &covgraph); +#[test] +fn test_covgraph_switchint_then_loop_else_return() { + let mir_body = switchint_then_loop_else_return(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz( + "covgraph_switchint_then_loop_else_return", + &mir_body, + &basic_coverage_blocks, + ); /* ┌─────────────────┐ │ bcb0: Call │ @@ -365,11 +425,26 @@ fn covgraph_switchint_then_loop_else_return() -> graph::CoverageGraph { │ │ └─────────────────────────────────────┘ */ - covgraph + assert_eq!( + basic_coverage_blocks.num_nodes(), + 4, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); } /// Create a mock `Body` with nested loops. -fn mir_switchint_loop_then_inner_loop_else_break() -> Body<'a> { +fn switchint_loop_then_inner_loop_else_break() -> Body<'a> { let mut blocks = MockBlocks::new(); let start = blocks.call(None); let switchint = blocks.switchint(Some(start)); @@ -438,13 +513,14 @@ fn mir_switchint_loop_then_inner_loop_else_break() -> Body<'a> { mir_body } -fn covgraph_switchint_loop_then_inner_loop_else_break() -> graph::CoverageGraph { - let mir_body = mir_switchint_loop_then_inner_loop_else_break(); - let covgraph = graph::CoverageGraph::from_mir(&mir_body); +#[test] +fn test_covgraph_switchint_loop_then_inner_loop_else_break() { + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); print_coverage_graphviz( "covgraph_switchint_loop_then_inner_loop_else_break", &mir_body, - &covgraph, + &basic_coverage_blocks, ); /* ┌─────────────────┐ @@ -477,23 +553,9 @@ fn covgraph_switchint_loop_then_inner_loop_else_break() -> graph::CoverageGraph │ │ └────────────────────────────────────────────┘ */ - covgraph -} - -macro_rules! assert_successors { - ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { - let mut successors = $basic_coverage_blocks.successors[$i].clone(); - successors.sort_unstable(); - assert_eq!(successors, vec![$($successor),*]); - } -} - -#[test] -fn test_covgraph_goto_switchint() { - let basic_coverage_blocks = covgraph_goto_switchint(); assert_eq!( basic_coverage_blocks.num_nodes(), - 3, + 7, "basic_coverage_blocks: {:?}", basic_coverage_blocks.iter_enumerated().collect::>() ); @@ -501,15 +563,24 @@ fn test_covgraph_goto_switchint() { let_bcb!(0); let_bcb!(1); let_bcb!(2); + let_bcb!(3); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); - assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); - assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); + assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); + assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); } #[test] fn test_find_loop_backedges_none() { - let basic_coverage_blocks = covgraph_goto_switchint(); + let mir_body = goto_switchint(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); if false { println!( "basic_coverage_blocks = {:?}", @@ -526,30 +597,10 @@ fn test_find_loop_backedges_none() { ); } -#[test] -fn test_covgraph_switchint_then_loop_else_return() { - let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); - assert_eq!( - basic_coverage_blocks.num_nodes(), - 4, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::>() - ); - - let_bcb!(0); - let_bcb!(1); - let_bcb!(2); - let_bcb!(3); - - assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); - assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); - assert_successors!(basic_coverage_blocks, bcb2, []); - assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); -} - #[test] fn test_find_loop_backedges_one() { - let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); + let mir_body = switchint_then_loop_else_return(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); let backedges = graph::find_loop_backedges(&basic_coverage_blocks); assert_eq!( backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), @@ -564,36 +615,10 @@ fn test_find_loop_backedges_one() { assert_eq!(backedges[bcb1], vec![bcb3]); } -#[test] -fn test_covgraph_switchint_loop_then_inner_loop_else_break() { - let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); - assert_eq!( - basic_coverage_blocks.num_nodes(), - 7, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::>() - ); - - let_bcb!(0); - let_bcb!(1); - let_bcb!(2); - let_bcb!(3); - let_bcb!(4); - let_bcb!(5); - let_bcb!(6); - - assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); - assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); - assert_successors!(basic_coverage_blocks, bcb2, []); - assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); - assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); - assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); - assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); -} - #[test] fn test_find_loop_backedges_two() { - let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); let backedges = graph::find_loop_backedges(&basic_coverage_blocks); assert_eq!( backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), @@ -613,7 +638,8 @@ fn test_find_loop_backedges_two() { #[test] fn test_traverse_coverage_with_loops() { - let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); let mut traversed_in_order = Vec::new(); let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks); while let Some(bcb) = traversal.next(&basic_coverage_blocks) { @@ -630,3 +656,58 @@ fn test_traverse_coverage_with_loops() { "bcb6 should not be visited until all nodes inside the first loop have been visited" ); } + +fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span { + let mut some_span: Option = None; + for (_, data) in mir_body.basic_blocks().iter_enumerated() { + let term_span = data.terminator().source_info.span; + if let Some(span) = some_span.as_mut() { + *span = span.to(term_span); + } else { + some_span = Some(term_span) + } + } + some_span.expect("body must have at least one BasicBlock") +} + +#[test] +fn test_make_bcb_counters() { + rustc_span::with_default_session_globals(|| { + let mir_body = goto_switchint(); + let body_span = synthesize_body_span_from_terminators(&mir_body); + let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let mut coverage_spans = Vec::new(); + for (bcb, data) in basic_coverage_blocks.iter_enumerated() { + if let Some(span) = + spans::filtered_terminator_span(data.terminator(&mir_body), body_span) + { + coverage_spans.push(spans::CoverageSpan::for_terminator(span, bcb, data.last_bb())); + } + } + let mut coverage_counters = counters::CoverageCounters::new(0); + let intermediate_expressions = coverage_counters + .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans) + .expect("should be Ok"); + assert_eq!(intermediate_expressions.len(), 0); + + let_bcb!(1); + assert_eq!( + 1, // coincidentally, bcb1 has a `Counter` with id = 1 + match basic_coverage_blocks[bcb1].counter().expect("should have a counter") { + CoverageKind::Counter { id, .. } => id, + _ => panic!("expected a Counter"), + } + .as_u32() + ); + + let_bcb!(2); + assert_eq!( + 2, // coincidentally, bcb2 has a `Counter` with id = 2 + match basic_coverage_blocks[bcb2].counter().expect("should have a counter") { + CoverageKind::Counter { id, .. } => id, + _ => panic!("expected a Counter"), + } + .as_u32() + ); + }); +} From 309d863e372227f9a3eed89e46cba0ff3b1ea8c8 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Fri, 13 Nov 2020 14:58:21 +0100 Subject: [PATCH 36/39] Fix wrong XPath --- src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index 0c047e6249a53..f895a4c2104ba 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,5 +1,7 @@ // ignore-tidy-linelength +#![crate_type="lib"] + pub mod internal { // @has 'raw_ident_eliminate_r_hashtag/internal/struct.mod.html' pub struct r#mod; @@ -8,13 +10,13 @@ pub mod internal { /// /// [name]: mod /// [other name]: crate::internal::mod - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' pub struct B; } /// See [name]. /// /// [name]: internal::mod -// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' -struct A; +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +pub struct A; From b4b0ef3e4b6ebf1571b2c38b952c940c8b94e91e Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 13 Nov 2020 09:02:25 -0800 Subject: [PATCH 37/39] Addressed feedback --- compiler/rustc_mir/src/transform/coverage/mod.rs | 2 +- .../src/transform/coverage/test_macros/src/lib.rs | 4 +--- .../rustc_mir/src/transform/coverage/tests.rs | 15 ++++++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index de37a67a1742f..192bb6680e420 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -34,7 +34,7 @@ use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; /// A simple error message wrapper for `coverage::Error`s. #[derive(Debug)] -pub(self) struct Error { +struct Error { message: String, } diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs index ea551c7745556..3d6095d2738cb 100644 --- a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs @@ -2,7 +2,5 @@ use proc_macro::TokenStream; #[proc_macro] pub fn let_bcb(item: TokenStream) -> TokenStream { - format!("let bcb{} = graph::BasicCoverageBlock::from_usize({}); let _ = {};", item, item, item) - .parse() - .unwrap() + format!("let bcb{} = graph::BasicCoverageBlock::from_usize({});", item, item).parse().unwrap() } diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 7de965be53bde..d36f1b8e5f670 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -35,6 +35,9 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; +// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`. +const TEMP_BLOCK: BasicBlock = BasicBlock::MAX; + fn dummy_ty() -> &'static TyS<'static> { thread_local! { static DUMMY_TYS: &'static TyS<'static> = Box::leak(box TyS::make_for_test( @@ -123,7 +126,7 @@ impl<'tcx> MockBlocks<'tcx> { if branch_index > branches.len() { branches.push((branches.len() as u128, old_otherwise)); while branches.len() < branch_index { - branches.push((branches.len() as u128, START_BLOCK)); + branches.push((branches.len() as u128, TEMP_BLOCK)); } to_block } else { @@ -143,7 +146,7 @@ impl<'tcx> MockBlocks<'tcx> { TerminatorKind::Call { func: Operand::Copy(self.dummy_place.clone()), args: vec![], - destination: Some((self.dummy_place.clone(), START_BLOCK)), + destination: Some((self.dummy_place.clone(), TEMP_BLOCK)), cleanup: None, from_hir_call: false, fn_span: DUMMY_SP, @@ -152,16 +155,14 @@ impl<'tcx> MockBlocks<'tcx> { } fn goto(&mut self, some_from_block: Option) -> BasicBlock { - self.add_block_from(some_from_block, TerminatorKind::Goto { target: START_BLOCK }) + self.add_block_from(some_from_block, TerminatorKind::Goto { target: TEMP_BLOCK }) } fn switchint(&mut self, some_from_block: Option) -> BasicBlock { - let move_ = |place: Place<'tcx>| Operand::Move(place); - let discriminant = Place::from(self.new_temp()); let switchint_kind = TerminatorKind::SwitchInt { - discr: move_(discriminant), + discr: Operand::Move(Place::from(self.new_temp())), switch_ty: dummy_ty(), - targets: SwitchTargets::static_if(0, START_BLOCK, START_BLOCK), + targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK), }; self.add_block_from(some_from_block, switchint_kind) } From bf6902ca61ae6e91a3ab95bed88426926f245d02 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 13 Nov 2020 10:23:50 -0800 Subject: [PATCH 38/39] Add BTreeMap::retain and BTreeSet::retain --- library/alloc/src/collections/btree/map.rs | 24 +++++++++++++++++++ .../alloc/src/collections/btree/map/tests.rs | 11 +++++++++ library/alloc/src/collections/btree/set.rs | 24 +++++++++++++++++++ .../alloc/src/collections/btree/set/tests.rs | 11 +++++++++ 4 files changed, 70 insertions(+) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 49122f53d33ad..7151d3763f01f 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -863,6 +863,30 @@ impl BTreeMap { } } + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_retain)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x*10)).collect(); + /// // Keep only the elements with even-numbered keys. + /// map.retain(|&k, _| k % 2 == 0); + /// assert!(map.into_iter().eq(vec![(0, 0), (2, 20), (4, 40), (6, 60)])); + /// ``` + #[inline] + #[unstable(feature = "btree_retain", issue = "79025")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + self.drain_filter(|k, v| !f(k, v)); + } + /// Moves all elements from `other` into `Self`, leaving `other` empty. /// /// # Examples diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index dd3ebcccf76a5..11dbb584abdac 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -808,6 +808,17 @@ fn test_range_mut() { map.check(); } +#[test] +fn test_retain() { + let mut map: BTreeMap = (0..100).map(|x| (x, x * 10)).collect(); + + map.retain(|&k, _| k % 2 == 0); + assert_eq!(map.len(), 50); + assert_eq!(map[&2], 20); + assert_eq!(map[&4], 40); + assert_eq!(map[&6], 60); +} + mod test_drain_filter { use super::*; diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 684019f8f5f5e..1a807100653bc 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -798,6 +798,30 @@ impl BTreeSet { Recover::take(&mut self.map, value) } + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_retain)] + /// use std::collections::BTreeSet; + /// + /// let xs = [1, 2, 3, 4, 5, 6]; + /// let mut set: BTreeSet = xs.iter().cloned().collect(); + /// // Keep only the even numbers. + /// set.retain(|&k| k % 2 == 0); + /// assert!(set.iter().eq([2, 4, 6].iter())); + /// ``` + #[unstable(feature = "btree_retain", issue = "79025")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.drain_filter(|v| !f(v)); + } + /// Moves all elements from `other` into `Self`, leaving `other` empty. /// /// # Examples diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 52cde8299e418..ef40a048a382e 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -324,6 +324,17 @@ fn test_is_subset() { assert_eq!(is_subset(&[99, 100], &large), false); } +#[test] +fn test_retain() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut set: BTreeSet = xs.iter().cloned().collect(); + set.retain(|&k| k % 2 == 0); + assert_eq!(set.len(), 3); + assert!(set.contains(&2)); + assert!(set.contains(&4)); + assert!(set.contains(&6)); +} + #[test] fn test_drain_filter() { let mut x: BTreeSet<_> = [1].iter().copied().collect(); From c50e57f946ee5a73b50fa5c52bb7a2a8a0cecf3f Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Fri, 13 Nov 2020 01:51:19 -0500 Subject: [PATCH 39/39] Log closure as well --- compiler/rustc_typeck/src/check/upvar.rs | 15 +- .../arrays-completely-captured.rs | 11 +- .../arrays-completely-captured.stderr | 34 ++++- .../capture-disjoint-field-struct.rs | 11 +- .../capture-disjoint-field-struct.stderr | 34 ++++- .../capture-disjoint-field-tuple.rs | 11 +- .../capture-disjoint-field-tuple.stderr | 34 ++++- .../2229_closure_analysis/capture-enums.rs | 28 ++-- .../capture-enums.stderr | 98 +++++++++---- .../destructure_patterns.rs | 37 +++-- .../destructure_patterns.stderr | 134 +++++++++++++----- .../feature-gate-capture_disjoint_fields.rs | 11 +- ...eature-gate-capture_disjoint_fields.stderr | 34 ++++- .../filter-on-struct-member.rs | 10 +- .../filter-on-struct-member.stderr | 20 ++- .../multilevel-path-1.rs | 11 +- .../multilevel-path-1.stderr | 34 ++++- .../multilevel-path-2.rs | 11 +- .../multilevel-path-2.stderr | 34 ++++- .../2229_closure_analysis/nested-closure.rs | 26 ++-- .../nested-closure.stderr | 78 +++++++--- .../path-with-array-access.rs | 11 +- .../path-with-array-access.stderr | 34 ++++- .../simple-struct-min-capture.rs | 13 +- .../simple-struct-min-capture.stderr | 41 ++++-- .../2229_closure_analysis/wild_patterns.rs | 29 ++-- .../wild_patterns.stderr | 102 +++++++++++-- 27 files changed, 727 insertions(+), 219 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 87365de90470d..e0bbe3cb07917 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -166,7 +166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information ); - self.log_closure_capture_info(closure_def_id, &delegate.capture_information, span); + self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure @@ -499,20 +499,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis) } - fn log_closure_capture_info( + fn log_capture_analysis_first_pass( &self, closure_def_id: rustc_hir::def_id::DefId, capture_information: &FxIndexMap, ty::CaptureInfo<'tcx>>, closure_span: Span, ) { if self.should_log_capture_analysis(closure_def_id) { + let mut diag = + self.tcx.sess.struct_span_err(closure_span, "First Pass analysis includes:"); for (place, capture_info) in capture_information { let capture_str = construct_capture_info_string(self.tcx, place, capture_info); let output_str = format!("Capturing {}", capture_str); let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - self.tcx.sess.span_err(span, &output_str); + diag.span_note(span, &output_str); } + diag.emit(); } } @@ -521,6 +524,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(min_captures) = self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) { + let mut diag = + self.tcx.sess.struct_span_err(closure_span, "Min Capture analysis includes:"); + for (_, min_captures_for_var) in min_captures { for capture in min_captures_for_var { let place = &capture.place; @@ -532,9 +538,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - self.tcx.sess.span_err(span, &output_str); + diag.span_note(span, &output_str); } } + diag.emit(); } } } diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs index 01c28aa29fb8e..131af6a10c898 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 #![feature(rustc_attrs)] // Ensure that capture analysis results in arrays being completely captured. @@ -8,10 +10,13 @@ fn main() { let mut c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: m[0] += 10; - //~^ ERROR: Capturing m[] -> MutBorrow - //~| ERROR: Min Capture m[] -> MutBorrow + //~^ NOTE: Capturing m[] -> MutBorrow + //~| NOTE: Min Capture m[] -> MutBorrow m[1] += 40; }; diff --git a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr index 228682caad78c..2a350f3033192 100644 --- a/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/arrays-completely-captured.rs:9:17 + --> $DIR/arrays-completely-captured.rs:11:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing m[] -> MutBorrow - --> $DIR/arrays-completely-captured.rs:12:9 +error: First Pass analysis includes: + --> $DIR/arrays-completely-captured.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | m[1] += 40; +LL | | }; + | |_____^ + | +note: Capturing m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 | LL | m[0] += 10; | ^ -error: Min Capture m[] -> MutBorrow - --> $DIR/arrays-completely-captured.rs:12:9 +error: Min Capture analysis includes: + --> $DIR/arrays-completely-captured.rs:14:5 + | +LL | / || { +LL | | +LL | | +LL | | m[0] += 10; +... | +LL | | m[1] += 40; +LL | | }; + | |_____^ + | +note: Min Capture m[] -> MutBorrow + --> $DIR/arrays-completely-captured.rs:17:9 | LL | m[0] += 10; | ^ diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs index b50c2d66d94b4..ba4955085372a 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Point { @@ -14,10 +16,13 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: println!("{}", p.x); - //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow }; // `c` should only capture `p.x`, therefore mutating `p.y` is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr index 41e641f356418..5fac6963afd32 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-disjoint-field-struct.rs:15:13 + --> $DIR/capture-disjoint-field-struct.rs:17:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-struct.rs:18:24 +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:23:24 | LL | println!("{}", p.x); | ^^^ -error: Min Capture p[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-struct.rs:18:24 +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-struct.rs:20:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-struct.rs:23:24 | LL | println!("{}", p.x); | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs index 99095c7198663..c1693fbad7986 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] fn main() { @@ -9,10 +11,13 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: println!("{}", t.0); - //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture t[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow }; // `c` only captures t.0, therefore mutating t.1 is allowed. diff --git a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr index 47470bb9646ea..1bfd63f2ace8c 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-disjoint-field-tuple.rs:10:13 + --> $DIR/capture-disjoint-field-tuple.rs:12:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing t[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-tuple.rs:13:24 +error: First Pass analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:18:24 | LL | println!("{}", t.0); | ^^^ -error: Min Capture t[(0, 0)] -> ImmBorrow - --> $DIR/capture-disjoint-field-tuple.rs:13:24 +error: Min Capture analysis includes: + --> $DIR/capture-disjoint-field-tuple.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", t.0); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/capture-disjoint-field-tuple.rs:18:24 | LL | println!("{}", t.0); | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs index 175fd79012446..8fb2f7f16d69c 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-enums.rs +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] enum Info { @@ -15,18 +17,21 @@ fn multi_variant_enum() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: if let Info::Point(_, _, str) = point { - //~^ Capturing point[] -> ImmBorrow - //~| Capturing point[(2, 0)] -> ByValue - //~| Min Capture point[] -> ByValue + //~^ NOTE: Capturing point[] -> ImmBorrow + //~| NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[] -> ByValue println!("{}", str); } if let Info::Meta(_, v) = meta { - //~^ Capturing meta[] -> ImmBorrow - //~| Capturing meta[(1, 1)] -> ByValue - //~| Min Capture meta[] -> ByValue + //~^ NOTE: Capturing meta[] -> ImmBorrow + //~| NOTE: Capturing meta[(1, 1)] -> ByValue + //~| NOTE: Min Capture meta[] -> ByValue println!("{:?}", v); } }; @@ -43,10 +48,13 @@ fn single_variant_enum() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { - let SingleVariant::Point(_, _, str) = point; - //~^ Capturing point[(2, 0)] -> ByValue - //~| Min Capture point[(2, 0)] -> ByValue + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let SingleVariant::Point(_, _, str) = point; + //~^ NOTE: Capturing point[(2, 0)] -> ByValue + //~| NOTE: Min Capture point[(2, 0)] -> ByValue println!("{}", str); }; diff --git a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr index 76a2de2faf9c8..ebe1dcb98848b 100644 --- a/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr +++ b/src/test/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/capture-enums.rs:16:13 + --> $DIR/capture-enums.rs:18:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/capture-enums.rs:44:13 + --> $DIR/capture-enums.rs:49:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,54 +25,98 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing point[] -> ImmBorrow - --> $DIR/capture-enums.rs:19:41 +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Capturing point[] -> ImmBorrow + --> $DIR/capture-enums.rs:24:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ - -error: Capturing point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:19:41 +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:24:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ - -error: Capturing meta[] -> ImmBorrow - --> $DIR/capture-enums.rs:26:35 +note: Capturing meta[] -> ImmBorrow + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ - -error: Capturing meta[(1, 1)] -> ByValue - --> $DIR/capture-enums.rs:26:35 +note: Capturing meta[(1, 1)] -> ByValue + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ -error: Min Capture point[] -> ByValue - --> $DIR/capture-enums.rs:19:41 +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:21:5 + | +LL | / || { +LL | | +LL | | +LL | | if let Info::Point(_, _, str) = point { +... | +LL | | } +LL | | }; + | |_____^ + | +note: Min Capture point[] -> ByValue + --> $DIR/capture-enums.rs:24:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ - -error: Min Capture meta[] -> ByValue - --> $DIR/capture-enums.rs:26:35 +note: Min Capture meta[] -> ByValue + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ -error: Capturing point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:47:43 +error: First Pass analysis includes: + --> $DIR/capture-enums.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ | -LL | let SingleVariant::Point(_, _, str) = point; - | ^^^^^ +note: Capturing point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:55:47 + | +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ -error: Min Capture point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:47:43 +error: Min Capture analysis includes: + --> $DIR/capture-enums.rs:52:5 + | +LL | / || { +LL | | +LL | | +LL | | let SingleVariant::Point(_, _, str) = point; +... | +LL | | println!("{}", str); +LL | | }; + | |_____^ + | +note: Min Capture point[(2, 0)] -> ByValue + --> $DIR/capture-enums.rs:55:47 | -LL | let SingleVariant::Point(_, _, str) = point; - | ^^^^^ +LL | let SingleVariant::Point(_, _, str) = point; + | ^^^^^ -error: aborting due to 10 previous errors; 1 warning emitted +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs index eef8fd6e55748..080ca0405b477 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] // Test to ensure Index projections are handled properly during capture analysis @@ -9,10 +11,13 @@ fn arrays() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let [a, b, .., e] = arr; - //~^ ERROR: Capturing arr[Index] -> ByValue - //~| ERROR: Min Capture arr[] -> ByValue + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue assert_eq!(a, "A"); assert_eq!(b, "B"); assert_eq!(e, "E"); @@ -32,12 +37,15 @@ fn structs() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let Point { x: ref mut x, y: _, id: moved_id } = p; - //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~| ERROR: Capturing p[(2, 0)] -> ByValue - //~| ERROR: Min Capture p[(0, 0)] -> MutBorrow - //~| ERROR: Min Capture p[(2, 0)] -> ByValue + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Capturing p[(2, 0)] -> ByValue + //~| NOTE: Min Capture p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(2, 0)] -> ByValue println!("{}, {}", x, moved_id); }; @@ -49,14 +57,17 @@ fn tuples() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let (ref mut x, ref ref_str, (moved_s, _)) = t; - //~^ ERROR: Capturing t[(0, 0)] -> MutBorrow - //~| ERROR: Capturing t[(1, 0)] -> ImmBorrow - //~| ERROR: Capturing t[(2, 0),(0, 0)] -> ByValue - //~| ERROR: Min Capture t[(0, 0)] -> MutBorrow - //~| ERROR: Min Capture t[(1, 0)] -> ImmBorrow - //~| ERROR: Min Capture t[(2, 0),(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0)] -> MutBorrow + //~| NOTE: Capturing t[(1, 0)] -> ImmBorrow + //~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue println!("{}, {} {}", x, ref_str, moved_s); }; diff --git a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr index 388cfd3da92c8..06ccc2d7a88b4 100644 --- a/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr +++ b/src/test/ui/closures/2229_closure_analysis/destructure_patterns.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:10:13 + --> $DIR/destructure_patterns.rs:12:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:33:13 + --> $DIR/destructure_patterns.rs:38:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/destructure_patterns.rs:50:13 + --> $DIR/destructure_patterns.rs:58:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,78 +34,144 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing arr[Index] -> ByValue - --> $DIR/destructure_patterns.rs:13:29 +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/destructure_patterns.rs:18:29 | LL | let [a, b, .., e] = arr; | ^^^ -error: Min Capture arr[] -> ByValue - --> $DIR/destructure_patterns.rs:13:29 +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:15:5 + | +LL | / || { +LL | | +LL | | +LL | | let [a, b, .., e] = arr; +... | +LL | | assert_eq!(e, "E"); +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/destructure_patterns.rs:18:29 | LL | let [a, b, .., e] = arr; | ^^^ -error: Capturing p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:36:58 +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ - -error: Capturing p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:36:58 +note: Capturing p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ -error: Min Capture p[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:36:58 +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:41:5 + | +LL | / || { +LL | | +LL | | +LL | | let Point { x: ref mut x, y: _, id: moved_id } = p; +... | +LL | | println!("{}, {}", x, moved_id); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ - -error: Min Capture p[(2, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:36:58 +note: Min Capture p[(2, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:44:58 | LL | let Point { x: ref mut x, y: _, id: moved_id } = p; | ^ -error: Capturing t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:53:54 +error: First Pass analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Capturing t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:53:54 +note: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Capturing t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:53:54 +note: Capturing t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ -error: Min Capture t[(0, 0)] -> MutBorrow - --> $DIR/destructure_patterns.rs:53:54 +error: Min Capture analysis includes: + --> $DIR/destructure_patterns.rs:61:5 + | +LL | / || { +LL | | +LL | | +LL | | let (ref mut x, ref ref_str, (moved_s, _)) = t; +... | +LL | | println!("{}, {} {}", x, ref_str, moved_s); +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> MutBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Min Capture t[(1, 0)] -> ImmBorrow - --> $DIR/destructure_patterns.rs:53:54 +note: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ - -error: Min Capture t[(2, 0),(0, 0)] -> ByValue - --> $DIR/destructure_patterns.rs:53:54 +note: Min Capture t[(2, 0),(0, 0)] -> ByValue + --> $DIR/destructure_patterns.rs:64:54 | LL | let (ref mut x, ref ref_str, (moved_s, _)) = t; | ^ -error: aborting due to 15 previous errors; 1 warning emitted +error: aborting due to 9 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs index ee55e3a3f2181..a3222635b626c 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] fn main() { @@ -7,9 +9,12 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("This uses new capture analyysis to capture s={}", s); - //~^ ERROR: Capturing s[] -> ImmBorrow - //~| ERROR: Min Capture s[] -> ImmBorrow + //~^ NOTE: Capturing s[] -> ImmBorrow + //~| NOTE: Min Capture s[] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr index 6d4d7ee5fbeb4..a031360ed34e1 100644 --- a/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13 + --> $DIR/feature-gate-capture_disjoint_fields.rs:10:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing s[] -> ImmBorrow - --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 +error: First Pass analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:16:69 | LL | println!("This uses new capture analyysis to capture s={}", s); | ^ -error: Min Capture s[] -> ImmBorrow - --> $DIR/feature-gate-capture_disjoint_fields.rs:11:69 +error: Min Capture analysis includes: + --> $DIR/feature-gate-capture_disjoint_fields.rs:13:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("This uses new capture analyysis to capture s={}", s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture s[] -> ImmBorrow + --> $DIR/feature-gate-capture_disjoint_fields.rs:16:69 | LL | println!("This uses new capture analyysis to capture s={}", s); | ^ diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs index d526934271c4b..9466e103897fb 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Filter { @@ -24,8 +26,10 @@ impl Data { self.list.retain( #[rustc_capture_analysis] |v| self.filter.allowed(*v), - //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow ); } } diff --git a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr index 452ba5c654516..e9420fe5a0c3a 100644 --- a/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -7,14 +7,26 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing self[Deref,(0, 0)] -> ImmBorrow - --> $DIR/filter-on-struct-member.rs:26:17 +error: First Pass analysis includes: + --> $DIR/filter-on-struct-member.rs:28:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:28:17 | LL | |v| self.filter.allowed(*v), | ^^^^^^^^^^^ -error: Min Capture self[Deref,(0, 0)] -> ImmBorrow - --> $DIR/filter-on-struct-member.rs:26:17 +error: Min Capture analysis includes: + --> $DIR/filter-on-struct-member.rs:28:13 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture self[Deref,(0, 0)] -> ImmBorrow + --> $DIR/filter-on-struct-member.rs:28:17 | LL | |v| self.filter.allowed(*v), | ^^^^^^^^^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs index 08c9aa8eff849..7d2d4c104d489 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] #![allow(unused)] @@ -21,10 +23,13 @@ fn main() { // Note that `wp.x` doesn't start off a variable defined outside the closure. let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: let wp = &w.p; - //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture w[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow println!("{}", wp.x); }; diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr index e8368201ede91..1c8db7952afe7 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/multilevel-path-1.rs:22:13 + --> $DIR/multilevel-path-1.rs:24:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing w[(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-1.rs:25:19 +error: First Pass analysis includes: + --> $DIR/multilevel-path-1.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:30:19 | LL | let wp = &w.p; | ^^^ -error: Min Capture w[(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-1.rs:25:19 +error: Min Capture analysis includes: + --> $DIR/multilevel-path-1.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | let wp = &w.p; +... | +LL | | println!("{}", wp.x); +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-1.rs:30:19 | LL | let wp = &w.p; | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs index 020753a71bf3f..540e70138e50e 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] #![allow(unused)] @@ -18,10 +20,13 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("{}", w.p.x); - //~^ ERROR: Capturing w[(0, 0),(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow }; // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`. diff --git a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr index dfcd2c7a08874..37287f6b3bc74 100644 --- a/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/multilevel-path-2.rs:19:13 + --> $DIR/multilevel-path-2.rs:21:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing w[(0, 0),(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-2.rs:22:24 +error: First Pass analysis includes: + --> $DIR/multilevel-path-2.rs:24:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:27:24 | LL | println!("{}", w.p.x); | ^^^^^ -error: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow - --> $DIR/multilevel-path-2.rs:22:24 +error: Min Capture analysis includes: + --> $DIR/multilevel-path-2.rs:24:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", w.p.x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow + --> $DIR/multilevel-path-2.rs:27:24 | LL | println!("{}", w.p.x); | ^^^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs index d2e99fe4accf6..88620550f2e7c 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ warning the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Point { @@ -20,20 +22,26 @@ fn main() { let mut c1 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("{}", p.x); - //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow let incr = 10; let mut c2 = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || p.y += incr; - //~^ ERROR: Capturing p[(1, 0)] -> MutBorrow - //~| ERROR: Capturing incr[] -> ImmBorrow - //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow - //~| ERROR: Min Capture incr[] -> ImmBorrow - //~| ERROR: Capturing p[(1, 0)] -> MutBorrow - //~| ERROR: Min Capture p[(1, 0)] -> MutBorrow + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Capturing incr[] -> ImmBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture incr[] -> ImmBorrow + //~| NOTE: Capturing p[(1, 0)] -> MutBorrow + //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow c2(); println!("{}", p.y); }; diff --git a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr index 2368a450bc6fb..21147be3f1d08 100644 --- a/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:21:18 + --> $DIR/nested-closure.rs:23:18 | LL | let mut c1 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let mut c1 = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/nested-closure.rs:28:22 + --> $DIR/nested-closure.rs:33:22 | LL | let mut c2 = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,54 +25,86 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:36:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ - -error: Capturing incr[] -> ImmBorrow - --> $DIR/nested-closure.rs:30:19 +note: Capturing incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:36:19 | LL | || p.y += incr; | ^^^^ -error: Min Capture p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:36:9 + | +LL | || p.y += incr; + | ^^^^^^^^^^^^^^ + | +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ - -error: Min Capture incr[] -> ImmBorrow - --> $DIR/nested-closure.rs:30:19 +note: Min Capture incr[] -> ImmBorrow + --> $DIR/nested-closure.rs:36:19 | LL | || p.y += incr; | ^^^^ -error: Capturing p[(0, 0)] -> ImmBorrow - --> $DIR/nested-closure.rs:24:24 +error: First Pass analysis includes: + --> $DIR/nested-closure.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | println!("{}", p.y); +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:29:24 | LL | println!("{}", p.x); | ^^^ - -error: Capturing p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +note: Capturing p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ -error: Min Capture p[(0, 0)] -> ImmBorrow - --> $DIR/nested-closure.rs:24:24 +error: Min Capture analysis includes: + --> $DIR/nested-closure.rs:26:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", p.x); +... | +LL | | println!("{}", p.y); +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/nested-closure.rs:29:24 | LL | println!("{}", p.x); | ^^^ - -error: Min Capture p[(1, 0)] -> MutBorrow - --> $DIR/nested-closure.rs:30:12 +note: Min Capture p[(1, 0)] -> MutBorrow + --> $DIR/nested-closure.rs:36:12 | LL | || p.y += incr; | ^^^ -error: aborting due to 10 previous errors; 1 warning emitted +error: aborting due to 6 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs index 4a42970137fff..16acd2f3206c9 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] struct Point { @@ -22,9 +24,12 @@ fn main() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: println!("{}", pent.points[5].x); - //~^ ERROR: Capturing pent[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture pent[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow }; } diff --git a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr index 7507e550adeb8..3c8d07ed9ba67 100644 --- a/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/path-with-array-access.rs:23:13 + --> $DIR/path-with-array-access.rs:25:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,14 +16,38 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing pent[(0, 0)] -> ImmBorrow - --> $DIR/path-with-array-access.rs:26:24 +error: First Pass analysis includes: + --> $DIR/path-with-array-access.rs:28:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:31:24 | LL | println!("{}", pent.points[5].x); | ^^^^^^^^^^^ -error: Min Capture pent[(0, 0)] -> ImmBorrow - --> $DIR/path-with-array-access.rs:26:24 +error: Min Capture analysis includes: + --> $DIR/path-with-array-access.rs:28:5 + | +LL | / || { +LL | | +LL | | +LL | | println!("{}", pent.points[5].x); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture pent[(0, 0)] -> ImmBorrow + --> $DIR/path-with-array-access.rs:31:24 | LL | println!("{}", pent.points[5].x); | ^^^^^^^^^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs index 68c18eac8048e..aaff3531e5850 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -1,7 +1,9 @@ // FIXME(arora-aman) add run-pass once 2229 is implemented #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] // Test to ensure that min analysis meets capture kind for all paths captured. @@ -24,12 +26,15 @@ fn main() { // let mut c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: p.x += 10; - //~^ ERROR: Capturing p[(0, 0)] -> MutBorrow - //~| ERROR: Min Capture p[] -> MutBorrow + //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow + //~| NOTE: Min Capture p[] -> MutBorrow println!("{:?}", p); - //~^ ERROR: Capturing p[] -> ImmBorrow + //~^ NOTE: Capturing p[] -> ImmBorrow }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr index 53cc681969ad0..30d3d5f504eb9 100644 --- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/simple-struct-min-capture.rs:25:17 + --> $DIR/simple-struct-min-capture.rs:27:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,24 +16,47 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(0, 0)] -> MutBorrow - --> $DIR/simple-struct-min-capture.rs:28:9 +error: First Pass analysis includes: + --> $DIR/simple-struct-min-capture.rs:30:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:33:9 | LL | p.x += 10; | ^^^ - -error: Capturing p[] -> ImmBorrow - --> $DIR/simple-struct-min-capture.rs:31:26 +note: Capturing p[] -> ImmBorrow + --> $DIR/simple-struct-min-capture.rs:36:26 | LL | println!("{:?}", p); | ^ -error: Min Capture p[] -> MutBorrow - --> $DIR/simple-struct-min-capture.rs:28:9 +error: Min Capture analysis includes: + --> $DIR/simple-struct-min-capture.rs:30:5 + | +LL | / || { +LL | | +LL | | +LL | | p.x += 10; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[] -> MutBorrow + --> $DIR/simple-struct-min-capture.rs:33:9 | LL | p.x += 10; | ^^^ -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 3 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs index 889836c11ce88..90b8033d074a1 100644 --- a/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.rs @@ -1,5 +1,7 @@ #![feature(capture_disjoint_fields)] -//~^ WARNING the feature `capture_disjoint_fields` is incomplete +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 #![feature(rustc_attrs)] // Test to ensure that we can handle cases where @@ -7,7 +9,9 @@ // using a Place expression // // Note: Currently when feature `capture_disjoint_fields` is enabled -// we can't handle such cases. So the test so the test +// we can't handle such cases. So the test current use `_x` instead of +// `_` until the issue is resolved. +// Check rust-lang/project-rfc-2229#24 for status. struct Point { x: i32, @@ -19,11 +23,14 @@ fn wild_struct() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: // FIXME(arora-aman): Change `_x` to `_` let Point { x: _x, y: _ } = p; - //~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow - //~| ERROR: Min Capture p[(0, 0)] -> ImmBorrow + //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow }; c(); @@ -34,11 +41,14 @@ fn wild_tuple() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: // FIXME(arora-aman): Change `_x` to `_` let (_x, _) = t; - //~^ ERROR: Capturing t[(0, 0)] -> ByValue - //~| ERROR: Min Capture t[(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue }; c(); @@ -49,11 +59,14 @@ fn wild_arr() { let c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: // FIXME(arora-aman): Change `_x` to `_` let [_x, _] = arr; - //~^ ERROR: Capturing arr[Index] -> ByValue - //~| ERROR: Min Capture arr[] -> ByValue + //~^ NOTE: Capturing arr[Index] -> ByValue + //~| NOTE: Min Capture arr[] -> ByValue }; c(); diff --git a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr index 621c8aeb790f9..36be8431be508 100644 --- a/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr +++ b/src/test/ui/closures/2229_closure_analysis/wild_patterns.stderr @@ -1,5 +1,5 @@ error[E0658]: attributes on expressions are experimental - --> $DIR/wild_patterns.rs:20:13 + --> $DIR/wild_patterns.rs:24:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/wild_patterns.rs:35:13 + --> $DIR/wild_patterns.rs:42:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable error[E0658]: attributes on expressions are experimental - --> $DIR/wild_patterns.rs:50:13 + --> $DIR/wild_patterns.rs:60:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,38 +34,110 @@ LL | #![feature(capture_disjoint_fields)] = note: `#[warn(incomplete_features)]` on by default = note: see issue #53488 for more information -error: Capturing p[(0, 0)] -> ImmBorrow - --> $DIR/wild_patterns.rs:24:37 +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:31:37 | LL | let Point { x: _x, y: _ } = p; | ^ -error: Min Capture p[(0, 0)] -> ImmBorrow - --> $DIR/wild_patterns.rs:24:37 +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:27:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture p[(0, 0)] -> ImmBorrow + --> $DIR/wild_patterns.rs:31:37 | LL | let Point { x: _x, y: _ } = p; | ^ -error: Capturing t[(0, 0)] -> ByValue - --> $DIR/wild_patterns.rs:39:23 +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:45:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:49:23 | LL | let (_x, _) = t; | ^ -error: Min Capture t[(0, 0)] -> ByValue - --> $DIR/wild_patterns.rs:39:23 +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:45:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/wild_patterns.rs:49:23 | LL | let (_x, _) = t; | ^ -error: Capturing arr[Index] -> ByValue - --> $DIR/wild_patterns.rs:54:23 +error: First Pass analysis includes: + --> $DIR/wild_patterns.rs:63:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing arr[Index] -> ByValue + --> $DIR/wild_patterns.rs:67:23 | LL | let [_x, _] = arr; | ^^^ -error: Min Capture arr[] -> ByValue - --> $DIR/wild_patterns.rs:54:23 +error: Min Capture analysis includes: + --> $DIR/wild_patterns.rs:63:5 + | +LL | / || { +LL | | +LL | | +LL | | // FIXME(arora-aman): Change `_x` to `_` +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture arr[] -> ByValue + --> $DIR/wild_patterns.rs:67:23 | LL | let [_x, _] = arr; | ^^^