diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ad926a810e6bf..3a746ebf8e9bd 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -610,6 +610,9 @@ declare_features! ( /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), 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 // ------------------------------------------------------------------------- @@ -633,6 +636,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 57ae534590ddf..77f694c56973a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -546,6 +546,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_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 216451f268f4e..f5ccd397c7ab5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -415,6 +415,11 @@ 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. + 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). pub generator_interior_types: Vec>, @@ -442,6 +447,7 @@ impl<'tcx> TypeckResults<'tcx> { tainted_by_errors: None, concrete_opaque_types: Default::default(), closure_captures: Default::default(), + closure_min_captures: Default::default(), generator_interior_types: Default::default(), } } @@ -676,6 +682,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { tainted_by_errors, ref concrete_opaque_types, ref closure_captures, + ref closure_min_captures, ref generator_interior_types, } = *self; @@ -709,6 +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_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 0a7bec1a342f3..bb9f5442f9a53 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,44 @@ 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 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 + 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_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_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_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 }, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1a6c45b6c80d2..b0576394e98e3 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, @@ -907,6 +908,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 e9dfef718fde9..1d46f54bd9dc3 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -39,10 +39,21 @@ 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, ProjectionKind}; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_span::sym; use rustc_span::{Span, Symbol}; -use std::collections::hash_map::Entry; + +/// 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>) { @@ -111,51 +122,18 @@ 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) - } - }; - - 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 capture_information = FxIndexMap::, ty::CaptureInfo<'tcx>>::default(); let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id()); assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); 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, @@ -166,6 +144,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .consume_body(body); + 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 // substs with the kind we inferred. @@ -182,7 +166,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.typeck_results.borrow_mut().upvar_capture_map.extend(delegate.adjust_upvar_captures); + 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 // variable is borrowed, and we know what traits the closure @@ -226,15 +213,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 +237,331 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect() } + + /// 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={:?}, 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>, + ) { + let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); + + 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), + }; + + // 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, + }; + + // 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); + } + } + + debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); + + if !root_var_min_capture_list.is_empty() { + self.typeck_results + .borrow_mut() + .closure_min_captures + .insert(closure_def_id, root_var_min_capture_list); + } + } + + 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 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"); + } + } + } + } + } + } + + 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> { @@ -258,6 +570,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 @@ -270,9 +586,28 @@ 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). - adjust_upvar_captures: ty::UpvarCaptureMap<'tcx>, + /// 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>>, } impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { @@ -314,26 +649,15 @@ 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[&place_with_id.place]; + let updated_info = self.fcx.determine_capture_info(curr_info, capture_info); + + self.capture_information[&place_with_id.place] = updated_info; } /// Indicates that `place_with_id` is being directly mutated (e.g., assigned @@ -349,7 +673,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 +687,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 +701,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 +731,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,37 +748,34 @@ 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 curr_capture_info = self.capture_information[&place_with_id.place]; + 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, curr_capture_info, kind ); - match upvar_capture { - ty::UpvarCapture::ByValue(_) => { - // Upvar is already by-value, the strongest criteria. - } - ty::UpvarCapture::ByRef(mut 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)); - } - // 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( @@ -501,6 +819,28 @@ 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 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 }; + + 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 +854,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 +872,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,10 +889,88 @@ 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); } } +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) } + +/// 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 57bd89b9d3da9..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 @@ -73,6 +72,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 +110,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { ) -> Self { ExprUseVisitor { mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), + body_owner, delegate, } } @@ -329,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) => { @@ -529,7 +530,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,50 +570,78 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { })); } - 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); - 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, - }; - 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 { - ty::UpvarCapture::ByValue(_) => { - let mode = copy_or_move(&self.mc, &captured_place); - self.delegate.consume(&captured_place, captured_place.hir_id, mode); - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow( - &captured_place, - captured_place.hir_id, - upvar_borrow.kind, - ); + 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); + + // 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; + + 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, + ); + } } } } } } - - 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/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..e144cce54ec22 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.rs @@ -0,0 +1,19 @@ +#![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; + //~^ ERROR: Capturing m[] -> MutBorrow + //~^^ ERROR: Min Capture m[] -> MutBorrow + 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..228682caad78c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr @@ -0,0 +1,33 @@ +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: 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/capture-disjoint-field-struct.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs new file mode 100644 index 0000000000000..3f2e16725dc38 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs @@ -0,0 +1,28 @@ +// 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); + //~^ 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. + 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..41e641f356418 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr @@ -0,0 +1,33 @@ +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: 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-tuple.rs b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs new file mode 100644 index 0000000000000..5db1c9c0683ec --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs @@ -0,0 +1,23 @@ +// 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); + //~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture t[(0, 0)] -> ImmBorrow + }; + + // `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..47470bb9646ea --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr @@ -0,0 +1,33 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/capture-disjoint-field-tuple.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 + +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:3:12 + | +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 + | +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/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/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..3713abe52df20 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs @@ -0,0 +1,15 @@ +#![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); + //~^ 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 new file mode 100644 index 0000000000000..6d4d7ee5fbeb4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr @@ -0,0 +1,33 @@ +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: 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/filter-on-struct-member.rs b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs new file mode 100644 index 0000000000000..3ef8b4acf9782 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.rs @@ -0,0 +1,41 @@ +// 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( + #[rustc_capture_analysis] + |v| self.filter.allowed(*v), + //~^ ERROR: Capturing self[Deref,(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture self[Deref,(0, 0)] -> ImmBorrow + ); + } +} + +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..452ba5c654516 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr @@ -0,0 +1,23 @@ +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:3:12 + | +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 + | +LL | |v| self.filter.allowed(*v), + | ^^^^^^^^^^^ + +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 + 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..a6793a43488e8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.rs @@ -0,0 +1,36 @@ +#![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; + //~^ ERROR: Capturing w[(0, 0)] -> ImmBorrow + //~^^ ERROR: Min Capture w[(0, 0)] -> ImmBorrow + 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..e8368201ede91 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-1.stderr @@ -0,0 +1,33 @@ +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: 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-2.rs b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs new file mode 100644 index 0000000000000..994e2530f7825 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.rs @@ -0,0 +1,32 @@ +// 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); + //~^ 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`. + 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..dfcd2c7a08874 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/multilevel-path-2.stderr @@ -0,0 +1,33 @@ +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: 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/nested-closure.rs b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs new file mode 100644 index 0000000000000..8641d6b4f59fb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.rs @@ -0,0 +1,48 @@ +// 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); + //~^ 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); + }; + + 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..2368a450bc6fb --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/nested-closure.stderr @@ -0,0 +1,78 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/nested-closure.rs:21: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:28: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:3:12 + | +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 + | +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/path-with-array-access.rs b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs new file mode 100644 index 0000000000000..48e0698b4a750 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.rs @@ -0,0 +1,30 @@ +#![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); + //~^ 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 new file mode 100644 index 0000000000000..7507e550adeb8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/path-with-array-access.stderr @@ -0,0 +1,33 @@ +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: 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/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs new file mode 100644 index 0000000000000..3d5a08344b72a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs @@ -0,0 +1,36 @@ +// 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; + //~^ 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 new file mode 100644 index 0000000000000..53cc681969ad0 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr @@ -0,0 +1,39 @@ +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: 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/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`. diff --git a/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs b/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs index 4fa5d54431c60..ad67a072cda58 100644 --- a/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs +++ b/src/test/ui/closures/closure-bounds-static-cant-capture-borrowed.rs @@ -4,7 +4,7 @@ fn bar(blk: F) where F: FnOnce() + 'static { fn foo(x: &()) { bar(|| { //~^ ERROR explicit lifetime required in the type of `x` [E0621] - let _ = x; + let _x = x; }) } diff --git a/src/test/ui/consts/const-eval/ub-upvars.rs b/src/test/ui/consts/const-eval/ub-upvars.rs index 5d19276557eb6..fa72939efd88a 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.rs +++ b/src/test/ui/consts/const-eval/ub-upvars.rs @@ -5,7 +5,7 @@ use std::mem; const BAD_UPVAR: &dyn FnOnce() = &{ //~ ERROR it is undefined behavior to use this value let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) }; let another_var = 13; - move || { let _ = bad_ref; let _ = another_var; } + move || { let _b = bad_ref; let _a = another_var; } }; fn main() {} diff --git a/src/test/ui/consts/const-eval/ub-upvars.stderr b/src/test/ui/consts/const-eval/ub-upvars.stderr index afd6c9035cabf..82425b12efb0b 100644 --- a/src/test/ui/consts/const-eval/ub-upvars.stderr +++ b/src/test/ui/consts/const-eval/ub-upvars.stderr @@ -4,7 +4,7 @@ error[E0080]: it is undefined behavior to use this value LL | / const BAD_UPVAR: &dyn FnOnce() = &{ LL | | let bad_ref: &'static u16 = unsafe { mem::transmute(0usize) }; LL | | let another_var = 13; -LL | | move || { let _ = bad_ref; let _ = another_var; } +LL | | move || { let _b = bad_ref; let _a = another_var; } LL | | }; | |__^ type validation failed: encountered a NULL reference at ... | diff --git a/src/test/ui/issues/issue-29948.rs b/src/test/ui/issues/issue-29948.rs index 8ede8143ea657..579c16acb673a 100644 --- a/src/test/ui/issues/issue-29948.rs +++ b/src/test/ui/issues/issue-29948.rs @@ -22,7 +22,7 @@ fn main() { let mut ran_drop = false; { let x = Foo(&mut ran_drop); - let x = move || { let _ = x; }; + let x = move || { let _x = x; }; f(x); } assert!(ran_drop); @@ -31,7 +31,7 @@ fn main() { { let x = Foo(&mut ran_drop); let result = panic::catch_unwind(move || { - let x = move || { let _ = x; panic!() }; + let x = move || { let _x = x; panic!() }; f(x); }); assert!(result.is_err()); diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 1a8258376142a..e1b446fc61f61 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -7,10 +7,10 @@ LL | let mut closure1 = || p = &y; = note: defining type: test::{closure#0}::{closure#0} with closure substs [ i16, extern "rust-call" fn(()), - (&'_#1r i32, &'_#2r mut &'_#3r i32), + (&'_#1r mut &'_#2r i32, &'_#3r i32), ] = note: number of external vids: 4 - = note: where '_#1r: '_#3r + = note: where '_#3r: '_#2r note: external requirements --> $DIR/escape-upvar-nested.rs:20:27 @@ -25,10 +25,10 @@ LL | | }; = note: defining type: test::{closure#0} with closure substs [ i16, extern "rust-call" fn(()), - (&'_#1r i32, &'_#2r mut &'_#3r i32), + (&'_#1r mut &'_#2r i32, &'_#3r i32), ] = note: number of external vids: 4 - = note: where '_#1r: '_#3r + = note: where '_#3r: '_#2r note: no external requirements --> $DIR/escape-upvar-nested.rs:13:1 diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 29fd796882b6a..0ea1076c32ef4 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -7,10 +7,10 @@ LL | let mut closure = || p = &y; = note: defining type: test::{closure#0} with closure substs [ i16, extern "rust-call" fn(()), - (&'_#1r i32, &'_#2r mut &'_#3r i32), + (&'_#1r mut &'_#2r i32, &'_#3r i32), ] = note: number of external vids: 4 - = note: where '_#1r: '_#3r + = note: where '_#3r: '_#2r note: no external requirements --> $DIR/escape-upvar-ref.rs:17:1