diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 8e8caa46c38..babab005edb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -661,11 +661,28 @@ pub struct UpvarBorrow<'tcx> { /// Part of `MinCaptureInformationMap`; List of `CapturePlace`s. pub type MinCaptureList<'tcx> = Vec>; -/// A `Place` and the corresponding `CaptureInfo`. +/// A composite describing a `Place` that is captured by a closure. #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)] pub struct CapturedPlace<'tcx> { + /// The `Place` that is captured. pub place: HirPlace<'tcx>, + + /// `CaptureKind` and expression(s) that resulted in such capture of `place`. pub info: CaptureInfo<'tcx>, + + /// Represents if `place` can be mutated or not. + pub mutability: hir::Mutability, +} + +impl CapturedPlace<'tcx> { + /// Returns the hir-id of the root variable for the captured place. + /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. + pub fn get_root_variable(&self) -> hir::HirId { + match self.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected upvar, found={:?}", base), + } + } } pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 6d98bf554f1..04ea3cbd8b6 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -215,6 +215,7 @@ fn append_place_to_string( PlaceRef { local, projection: [proj_base @ .., elem] } => { match elem { ProjectionElem::Deref => { + // FIXME(project-rfc_2229#36): print capture precisely here. let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let var_index = field.index(); @@ -259,6 +260,7 @@ fn append_place_to_string( ProjectionElem::Field(field, _ty) => { autoderef = true; + // FIXME(project-rfc_2229#36): print capture precisely here. let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let var_index = field.index(); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index 350e0d045fa..fb7694b7d88 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -345,7 +345,9 @@ fn report_cannot_move_from_borrowed_content( }; let upvar = &self.upvars[upvar_field.unwrap().index()]; - let upvar_hir_id = upvar.var_hir_id; + // FIXME(project-rfc-2229#8): Improve borrow-check diagnostics in case of precise + // capture. + let upvar_hir_id = upvar.place.get_root_variable(); let upvar_name = upvar.name; let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index 73196c732f5..74abe2d35ee 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -64,12 +64,29 @@ pub(crate) fn report_mutability_error( Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); - item_msg = format!("`{}`", access_place_desc.unwrap()); - if self.is_upvar_field_projection(access_place.as_ref()).is_some() { - reason = ", as it is not declared as mutable".to_string(); + let imm_borrow_derefed = self.upvars[upvar_index.index()] + .place + .place + .deref_tys() + .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not))); + + // If the place is immutable then: + // + // - Either we deref a immutable ref to get to our final place. + // - We don't capture derefs of raw ptrs + // - Or the final place is immut because the root variable of the capture + // isn't marked mut and we should suggest that to the user. + if imm_borrow_derefed { + // If we deref an immutable ref then the suggestion here doesn't help. + return; } else { - let name = self.upvars[upvar_index.index()].name; - reason = format!(", as `{}` is not declared as mutable", name); + item_msg = format!("`{}`", access_place_desc.unwrap()); + if self.is_upvar_field_projection(access_place.as_ref()).is_some() { + reason = ", as it is not declared as mutable".to_string(); + } else { + let name = self.upvars[upvar_index.index()].name; + reason = format!(", as `{}` is not declared as mutable", name); + } } } @@ -259,9 +276,12 @@ pub(crate) fn report_mutability_error( Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty )); + let captured_place = &self.upvars[upvar_index.index()].place; + err.span_label(span, format!("cannot {ACT}", ACT = act)); - let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id; + let upvar_hir_id = captured_place.get_root_variable(); + if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) { if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs index a850b85e9bb..4abc623fc5f 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs @@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { tcx: TyCtxt<'tcx>, body: &Body<'tcx>, local_names: &IndexVec>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], fr: RegionVid, ) -> Option<(Option, Span)> { debug!("get_var_name_and_span_for_region(fr={:?})", fr); @@ -21,6 +21,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("get_var_name_and_span_for_region: attempting upvar"); self.get_upvar_index_for_region(tcx, fr) .map(|index| { + // FIXME(project-rfc-2229#8): Use place span for diagnostics let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index); (Some(name), span) }) @@ -59,10 +60,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { crate fn get_upvar_name_and_span_for_region( &self, tcx: TyCtxt<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], upvar_index: usize, ) -> (Symbol, Span) { - let upvar_hir_id = upvars[upvar_index].var_hir_id; + let upvar_hir_id = upvars[upvar_index].place.get_root_variable(); debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id); let upvar_name = tcx.hir().name(upvar_hir_id); diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index 7c7edfdb5fb..5db52db70ac 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -5,11 +5,10 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, Node}; +use rustc_hir::Node; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::mir::{ traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents, @@ -18,7 +17,7 @@ use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -73,16 +72,14 @@ // FIXME(eddyb) perhaps move this somewhere more centrally. #[derive(Debug)] -crate struct Upvar { +crate struct Upvar<'tcx> { + // FIXME(project-rfc_2229#36): print capture precisely here. name: Symbol, - // FIXME(project-rfc-2229#8): This should use Place or something similar - var_hir_id: HirId, + place: CapturedPlace<'tcx>, /// If true, the capture is behind a reference. by_ref: bool, - - mutability: Mutability, } const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref]; @@ -161,26 +158,13 @@ fn do_mir_borrowck<'a, 'tcx>( let upvars: Vec<_> = tables .closure_min_captures_flattened(def.did.to_def_id()) .map(|captured_place| { - let var_hir_id = match captured_place.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - _ => bug!("Expected upvar"), - }; + let var_hir_id = captured_place.get_root_variable(); let capture = captured_place.info.capture_kind; let by_ref = match capture { ty::UpvarCapture::ByValue(_) => false, ty::UpvarCapture::ByRef(..) => true, }; - let mut upvar = Upvar { - name: tcx.hir().name(var_hir_id), - var_hir_id, - by_ref, - mutability: Mutability::Not, - }; - let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - if bm == ty::BindByValue(hir::Mutability::Mut) { - upvar.mutability = Mutability::Mut; - } - upvar + Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref } }) .collect(); @@ -549,7 +533,7 @@ fn do_mir_borrowck<'a, 'tcx>( dominators: Dominators, /// Information about upvars not necessarily preserved in types or MIR - upvars: Vec, + upvars: Vec>, /// Names of local (user) variables (extracted from `var_debug_info`). local_names: IndexVec>, @@ -1374,13 +1358,38 @@ fn consume_rvalue( fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) { let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| { - if !place.projection.is_empty() { - if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { - this.used_mut_upvars.push(field); - } - } else { - this.used_mut.insert(place.local); + // We have three possibilities here: + // a. We are modifying something through a mut-ref + // b. We are modifying something that is local to our parent + // c. Current body is a nested closure, and we are modifying path starting from + // a Place captured by our parent closure. + + // Handle (c), the path being modified is exactly the path captured by our parent + if let Some(field) = this.is_upvar_field_projection(place.as_ref()) { + this.used_mut_upvars.push(field); + return; } + + for (place_ref, proj) in place.iter_projections().rev() { + // Handle (a) + if proj == ProjectionElem::Deref { + match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() { + // We aren't modifying a variable directly + ty::Ref(_, _, hir::Mutability::Mut) => return, + + _ => {} + } + } + + // Handle (c) + if let Some(field) = this.is_upvar_field_projection(place_ref) { + this.used_mut_upvars.push(field); + return; + } + } + + // Handle(b) + this.used_mut.insert(place.local); }; // This relies on the current way that by-value @@ -2146,6 +2155,7 @@ fn is_mutable( place: PlaceRef<'tcx>, is_local_mutation_allowed: LocalMutationIsAllowed, ) -> Result, PlaceRef<'tcx>> { + debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed); match place.last_projection() { None => { let local = &self.body.local_decls[place.local]; @@ -2227,11 +2237,11 @@ fn is_mutable( if let Some(field) = upvar_field_projection { let upvar = &self.upvars[field.index()]; debug!( - "upvar.mutability={:?} local_mutation_is_allowed={:?} \ - place={:?}", - upvar, is_local_mutation_allowed, place + "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \ + place={:?}, place_base={:?}", + upvar, is_local_mutation_allowed, place, place_base ); - match (upvar.mutability, is_local_mutation_allowed) { + match (upvar.place.mutability, is_local_mutation_allowed) { ( Mutability::Not, LocalMutationIsAllowed::No diff --git a/compiler/rustc_mir/src/borrow_check/nll.rs b/compiler/rustc_mir/src/borrow_check/nll.rs index 359c5f261a4..a0265b20d12 100644 --- a/compiler/rustc_mir/src/borrow_check/nll.rs +++ b/compiler/rustc_mir/src/borrow_check/nll.rs @@ -165,7 +165,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], ) -> NllOutput<'tcx> { let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); diff --git a/compiler/rustc_mir/src/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs index fa3ae2367e0..80de3b4e363 100644 --- a/compiler/rustc_mir/src/borrow_check/path_utils.rs +++ b/compiler/rustc_mir/src/borrow_check/path_utils.rs @@ -143,7 +143,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool { /// of a closure type. pub(crate) fn is_upvar_field_projection( tcx: TyCtxt<'tcx>, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], place_ref: PlaceRef<'tcx>, body: &Body<'tcx>, ) -> Option { diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index fb9820e853f..24bbd2b8c49 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -132,7 +132,7 @@ pub(crate) fn type_check<'mir, 'tcx>( flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>, move_data: &MoveData<'tcx>, elements: &Rc, - upvars: &[Upvar], + upvars: &[Upvar<'tcx>], ) -> MirTypeckResults<'tcx> { let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body)); let mut constraints = MirTypeckRegionConstraints { @@ -821,7 +821,7 @@ struct BorrowCheckContext<'a, 'tcx> { all_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - upvars: &'a [Upvar], + upvars: &'a [Upvar<'tcx>], } crate struct MirTypeckResults<'tcx> { @@ -2490,7 +2490,9 @@ fn add_reborrow_constraint( body, ); let category = if let Some(field) = field { - ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id) + let var_hir_id = self.borrowck_context.upvars[field.index()].place.get_root_variable(); + // FIXME(project-rfc-2229#8): Use Place for better diagnostics + ConstraintCategory::ClosureUpvar(var_hir_id) } else { ConstraintCategory::Boring }; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 99661599525..e4891eb5a3c 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -851,22 +851,13 @@ fn args_and_body( _ => bug!("Expected an upvar") }; - let mut mutability = Mutability::Not; + let mutability = captured_place.mutability; // FIXME(project-rfc-2229#8): Store more precise information let mut name = kw::Empty; if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) { if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { name = ident.name; - match hir_typeck_results - .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - Some(ty::BindByValue(hir::Mutability::Mut)) => { - mutability = Mutability::Mut; - } - Some(_) => mutability = Mutability::Not, - _ => {} - } } } diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 6b2cba62fa6..f039445bf77 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -184,10 +184,9 @@ fn analyze_closure( let origin = if self.tcx.features().capture_disjoint_fields { origin } else { - // FIXME(project-rfc-2229#26): Once rust-lang#80092 is merged, we should restrict the - // precision of origin as well. Otherwise, this will cause issues when project-rfc-2229#26 - // is fixed as we might see Index projections in the origin, which we can't print because - // we don't store enough information. + // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are + // made, make sure we are selecting and restricting + // the origin correctly. (origin.0, Place { projections: vec![], ..origin.1 }) }; @@ -252,8 +251,8 @@ fn final_upvar_tys(&self, closure_id: DefId) -> Vec> { let capture = captured_place.info.capture_kind; debug!( - "place={:?} upvar_ty={:?} capture={:?}", - captured_place.place, upvar_ty, capture + "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", + captured_place.place, upvar_ty, capture, captured_place.mutability, ); match capture { @@ -419,19 +418,13 @@ fn compute_min_captures( 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 place = restrict_capture_precision(place, capture_info.capture_kind); let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { - let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info }]; + let mutability = self.determine_capture_mutability(&place); + let min_cap_list = + vec![ty::CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; } @@ -494,8 +487,9 @@ fn compute_min_captures( // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { + let mutability = self.determine_capture_mutability(&place); let captured_place = - ty::CapturedPlace { place: place.clone(), info: updated_capture_info }; + ty::CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); } } @@ -615,6 +609,49 @@ fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span } } } + + /// A captured place is mutable if + /// 1. Projections don't include a Deref of an immut-borrow, **and** + /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. + fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability { + let var_hir_id = match place.base { + PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + _ => unreachable!(), + }; + + let bm = *self + .typeck_results + .borrow() + .pat_binding_modes() + .get(var_hir_id) + .expect("missing binding mode"); + + let mut is_mutbl = match bm { + ty::BindByValue(mutability) => mutability, + ty::BindByReference(_) => hir::Mutability::Not, + }; + + for pointer_ty in place.deref_tys() { + match pointer_ty.kind() { + // We don't capture derefs of raw ptrs + ty::RawPtr(_) => unreachable!(), + + // Derefencing a mut-ref allows us to mut the Place if we don't deref + // an immut-ref after on top of this. + ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut, + + // The place isn't mutable once we dereference a immutable reference. + ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not, + + // Dereferencing a box doesn't change mutability + ty::Adt(def, ..) if def.is_box() => {} + + unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty), + } + } + + is_mutbl + } } struct InferBorrowKind<'a, 'tcx> { @@ -960,6 +997,66 @@ fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::H } } +/// Truncate projections so that following rules are obeyed by the captured `place`: +/// +/// - No Derefs in move closure, this will result in value behind a reference getting moved. +/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture +/// them completely. +/// - No Index projections are captured, since arrays are captured completely. +fn restrict_capture_precision<'tcx>( + mut place: Place<'tcx>, + capture_kind: ty::UpvarCapture<'tcx>, +) -> Place<'tcx> { + if place.projections.is_empty() { + // Nothing to do here + return place; + } + + if place.base_ty.is_unsafe_ptr() { + place.projections.truncate(0); + return place; + } + + let mut truncated_length = usize::MAX; + let mut first_deref_projection = usize::MAX; + + for (i, proj) in place.projections.iter().enumerate() { + if proj.ty.is_unsafe_ptr() { + // Don't apply any projections on top of an unsafe ptr + truncated_length = truncated_length.min(i + 1); + break; + } + match proj.kind { + ProjectionKind::Index => { + // Arrays are completely captured, so we drop Index projections + truncated_length = truncated_length.min(i); + break; + } + ProjectionKind::Deref => { + // We only drop Derefs in case of move closures + // There might be an index projection or raw ptr ahead, so we don't stop here. + first_deref_projection = first_deref_projection.min(i); + } + ProjectionKind::Field(..) => {} // ignore + ProjectionKind::Subslice => {} // We never capture this + } + } + + let length = place + .projections + .len() + .min(truncated_length) + // In case of capture `ByValue` we want to not capture derefs + .min(match capture_kind { + ty::UpvarCapture::ByValue(..) => first_deref_projection, + ty::UpvarCapture::ByRef(..) => usize::MAX, + }); + + place.projections.truncate(length); + + place +} + fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { let variable_name = match place.base { PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.rs b/src/test/ui/closures/2229_closure_analysis/by_value.rs new file mode 100644 index 00000000000..1007fb582e5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/by_value.rs @@ -0,0 +1,41 @@ +// Test that we handle derferences properly when only some of the captures are being moved with +// `capture_disjoint_fields` enabled. + + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +#[derive(Debug, Default)] +struct SomeLargeType; +struct MuchLargerType([SomeLargeType; 32]); + +// Ensure that we don't capture any derefs when moving captures into the closures, +// i.e. only data from the enclosing stack is moved. +fn big_box() { + let s = MuchLargerType(Default::default()); + let b = Box::new(s); + let t = (b, 10); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || { + //~^ First Pass analysis includes: + //~| Min Capture analysis includes: + let p = t.0.0; + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + println!("{} {:?}", t.1, p); + //~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow + }; + + c(); +} + +fn main() { + big_box(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/by_value.stderr new file mode 100644 index 00000000000..fe04dbef6d8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/by_value.stderr @@ -0,0 +1,67 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/by_value.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/by_value.rs:5:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/by_value.rs:25:5 + | +LL | / || { +LL | | +LL | | +LL | | let p = t.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + --> $DIR/by_value.rs:28:17 + | +LL | let p = t.0.0; + | ^^^^^ +note: Capturing t[(1, 0)] -> ImmBorrow + --> $DIR/by_value.rs:31:29 + | +LL | println!("{} {:?}", t.1, p); + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/by_value.rs:25:5 + | +LL | / || { +LL | | +LL | | +LL | | let p = t.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/by_value.rs:28:17 + | +LL | let p = t.0.0; + | ^^^^^ +note: Min Capture t[(1, 0)] -> ImmBorrow + --> $DIR/by_value.rs:31:29 + | +LL | println!("{} {:?}", t.1, 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/diagnostics/cant-mutate-imm-borrow.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs new file mode 100644 index 00000000000..1ea38e260b6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs @@ -0,0 +1,20 @@ +// Test that if we deref an immutable borrow to access a Place, +// then we can't mutate the final place. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +fn main() { + let mut x = (format!(""), format!("X2")); + let mut y = (&x, "Y"); + let z = (&mut y, "Z"); + + // `x.0` is mutable but we access `x` via `z.0.0`, which is an immutable reference and + // therefore can't be mutated. + let mut c = || { + //~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference + z.0.0.0 = format!("X1"); + }; + + c(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr new file mode 100644 index 00000000000..861bc44b78d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr @@ -0,0 +1,21 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/cant-mutate-imm-borrow.rs:4:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference + --> $DIR/cant-mutate-imm-borrow.rs:14:17 + | +LL | let mut c = || { + | ^^ cannot borrow as mutable +LL | +LL | z.0.0.0 = format!("X1"); + | - mutable borrow occurs due to use of `z.0.0.0` in closure + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs new file mode 100644 index 00000000000..997ecc7dddd --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs @@ -0,0 +1,35 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +// Ensure that diagnostics for mutability error (because the root variable +// isn't mutable) work with `capture_disjoint_fields` enabled. + +fn mut_error_struct() { + let x = (10, 10); + let y = (x, 10); + let z = (y, 10); + + let mut c = || { + z.0.0.0 = 20; + //~^ ERROR: cannot assign to `z`, as it is not declared as mutable + }; + + c(); +} + +fn mut_error_box() { + let x = (10, 10); + let bx = Box::new(x); + + let mut c = || { + bx.0 = 20; + //~^ ERROR: cannot assign to `bx`, as it is not declared as mutable + }; + + c(); +} + +fn main() { + mut_error_struct(); + mut_error_box(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr new file mode 100644 index 00000000000..5e15635ac6e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr @@ -0,0 +1,30 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/cant-mutate-imm.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0594]: cannot assign to `z`, as it is not declared as mutable + --> $DIR/cant-mutate-imm.rs:13:9 + | +LL | let z = (y, 10); + | - help: consider changing this to be mutable: `mut z` +... +LL | z.0.0.0 = 20; + | ^^^^^^^^^^^^ cannot assign + +error[E0594]: cannot assign to `bx`, as it is not declared as mutable + --> $DIR/cant-mutate-imm.rs:25:9 + | +LL | let bx = Box::new(x); + | -- help: consider changing this to be mutable: `mut bx` +... +LL | bx.0 = 20; + | ^^^^^^^^^ cannot assign + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0594`. diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs new file mode 100644 index 00000000000..676fde558df --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs @@ -0,0 +1,38 @@ +// Test that we can't mutate a place if we need to deref an imm-borrow +// to reach it. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +fn imm_mut_ref() { + let mut x = String::new(); + let y = String::new(); + let mref_x = &mut x; + let ref_mref_x = &mref_x; + + let c = || { + //~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference + **ref_mref_x = y; + }; + + c(); +} + +fn mut_imm_ref() { + let x = String::new(); + let y = String::new(); + let mut ref_x = &x; + let mref_ref_x = &mut ref_x; + + let c = || { + //~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference + **mref_ref_x = y; + }; + + c(); +} + +fn main() { + imm_mut_ref(); + mut_imm_ref(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr new file mode 100644 index 00000000000..8cb2ed2235d --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr @@ -0,0 +1,33 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/mut_ref.rs:4:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference + --> $DIR/mut_ref.rs:13:13 + | +LL | let ref_mref_x = &mref_x; + | ------- help: consider changing this to be a mutable reference: `&mut mref_x` +LL | +LL | let c = || { + | ^^ `ref_mref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable +LL | +LL | **ref_mref_x = y; + | ---------- mutable borrow occurs due to use of `**ref_mref_x` in closure + +error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference + --> $DIR/mut_ref.rs:27:13 + | +LL | let c = || { + | ^^ cannot borrow as mutable +LL | +LL | **mref_ref_x = y; + | ---------- mutable borrow occurs due to use of `**mref_ref_x` in closure + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/move_closure.rs new file mode 100644 index 00000000000..8bdc999ca3c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs @@ -0,0 +1,72 @@ +// Test that move closures drop derefs with `capture_disjoint_fields` enabled. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| NOTE: `#[warn(incomplete_features)]` on by default +//~| NOTE: see issue #53488 +#![feature(rustc_attrs)] + +// Test we truncate derefs properly +fn simple_ref() { + let mut s = 10; + let ref_s = &mut s; + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + *ref_s += 10; + //~^ NOTE: Capturing ref_s[Deref] -> ByValue + //~| NOTE: Min Capture ref_s[] -> ByValue + }; + c(); +} + +// Test we truncate derefs properly +fn struct_contains_ref_to_another_struct() { + struct S(String); + struct T<'a>(&'a mut S); + + let mut s = S("s".into()); + let t = T(&mut s); + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + t.0.0 = "new s".into(); + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue + }; + + c(); +} + +// Test that we don't reduce precision when there is nothing deref. +fn no_ref() { + struct S(String); + struct T(S); + + let t = T(S("s".into())); + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + t.0.0 = "new S".into(); + //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue + }; + c(); +} + +fn main() { + simple_ref(); + struct_contains_ref_to_another_struct(); + no_ref(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr new file mode 100644 index 00000000000..a745f14598e --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr @@ -0,0 +1,147 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:14: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 + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:35: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 + +error[E0658]: attributes on expressions are experimental + --> $DIR/move_closure.rs:55: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/move_closure.rs:3:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:17:5 + | +LL | / move || { +LL | | +LL | | +LL | | *ref_s += 10; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing ref_s[Deref] -> ByValue + --> $DIR/move_closure.rs:20:9 + | +LL | *ref_s += 10; + | ^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:17:5 + | +LL | / move || { +LL | | +LL | | +LL | | *ref_s += 10; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture ref_s[] -> ByValue + --> $DIR/move_closure.rs:20:9 + | +LL | *ref_s += 10; + | ^^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:38:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new s".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + --> $DIR/move_closure.rs:41:9 + | +LL | t.0.0 = "new s".into(); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:38:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new s".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/move_closure.rs:41:9 + | +LL | t.0.0 = "new s".into(); + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:58:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new S".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),(0, 0)] -> ByValue + --> $DIR/move_closure.rs:61:9 + | +LL | t.0.0 = "new S".into(); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:58:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new S".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0),(0, 0)] -> ByValue + --> $DIR/move_closure.rs:61:9 + | +LL | t.0.0 = "new S".into(); + | ^^^^^ + +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/run_pass/by_value.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs new file mode 100644 index 00000000000..9a93e6cf1e1 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs @@ -0,0 +1,28 @@ +// run-pass + +// Test that ByValue captures compile sucessefully especially when the captures are +// derefenced within the closure. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +#[derive(Debug, Default)] +struct SomeLargeType; +struct MuchLargerType([SomeLargeType; 32]); + +fn big_box() { + let s = MuchLargerType(Default::default()); + let b = Box::new(s); + let t = (b, 10); + + let c = || { + let p = t.0.0; + println!("{} {:?}", t.1, p); + }; + + c(); +} + +fn main() { + big_box(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr new file mode 100644 index 00000000000..98715c6b943 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/by_value.rs:6:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs new file mode 100644 index 00000000000..4007a5a48aa --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs @@ -0,0 +1,64 @@ +// run-pass + +// Test that move closures compile properly with `capture_disjoint_fields` enabled. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +fn simple_ref() { + let mut s = 10; + let ref_s = &mut s; + + let mut c = move || { + *ref_s += 10; + }; + c(); +} + +fn struct_contains_ref_to_another_struct() { + struct S(String); + struct T<'a>(&'a mut S); + + let mut s = S("s".into()); + let t = T(&mut s); + + let mut c = move || { + t.0.0 = "new s".into(); + }; + + c(); +} + +#[derive(Debug)] +struct S(String); + +#[derive(Debug)] +struct T(S); + +fn no_ref() { + let mut t = T(S("s".into())); + let mut c = move || { + t.0.0 = "new S".into(); + }; + c(); +} + +fn no_ref_nested() { + let mut t = T(S("s".into())); + let c = || { + println!("{:?}", t.0); + let mut c = move || { + t.0.0 = "new S".into(); + println!("{:?}", t.0.0); + }; + c(); + }; + c(); +} + +fn main() { + simple_ref(); + struct_contains_ref_to_another_struct(); + no_ref(); + no_ref_nested(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr new file mode 100644 index 00000000000..c1d8ba575d6 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/move_closure.rs:5:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs new file mode 100644 index 00000000000..315622443c3 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs @@ -0,0 +1,56 @@ +// run-pass + +// Test that we can mutate a place through a mut-borrow +// that is captured by the closure + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +// Check that we can mutate when one deref is required +fn mut_ref_1() { + let mut x = String::new(); + let rx = &mut x; + + let mut c = || { + *rx = String::new(); + }; + + c(); +} + +// Similar example as mut_ref_1, we don't deref the imm-borrow here, +// and so we are allowed to mutate. +fn mut_ref_2() { + let x = String::new(); + let y = String::new(); + let mut ref_x = &x; + let m_ref_x = &mut ref_x; + + let mut c = || { + *m_ref_x = &y; + }; + + c(); +} + +// Check that we can mutate when multiple derefs of mut-borrows are required to reach +// the target place. +// It works because all derefs are mutable, if either of them was an immutable +// borrow, then we would not be able to deref. +fn mut_mut_ref() { + let mut x = String::new(); + let mut mref_x = &mut x; + let m_mref_x = &mut mref_x; + + let mut c = || { + **m_mref_x = String::new(); + }; + + c(); +} + +fn main() { + mut_ref_1(); + mut_ref_2(); + mut_mut_ref(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr new file mode 100644 index 00000000000..4b37a0b405f --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/mut_ref.rs:6:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs new file mode 100644 index 00000000000..2dba923647a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs @@ -0,0 +1,45 @@ +// run-pass + +// Test that we can mutate a place through a mut-borrow +// that is captured by the closure + +// More specifically we test that the if the mutable reference isn't root variable of a capture +// but rather accessed while acessing the precise capture. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +fn mut_tuple() { + let mut t = (10, 10); + + let t1 = (&mut t, 10); + + let mut c = || { + // Mutable because (*t.0) is mutable + t1.0.0 += 10; + }; + + c(); +} + +fn mut_tuple_nested() { + let mut t = (10, 10); + + let t1 = (&mut t, 10); + + let mut c = || { + let mut c = || { + // Mutable because (*t.0) is mutable + t1.0.0 += 10; + }; + + c(); + }; + + c(); +} + +fn main() { + mut_tuple(); + mut_tuple_nested(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr new file mode 100644 index 00000000000..418ab29098b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/mut_ref_struct_mem.rs:9:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs new file mode 100644 index 00000000000..f6e9862b26c --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs @@ -0,0 +1,47 @@ +// run-pass + +// Test that we can use raw ptrs when using `capture_disjoint_fields`. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete + +#[derive(Debug)] +struct S { + s: String, + t: String, +} + +struct T(*const S); + +fn unsafe_imm() { + let s = "".into(); + let t = "".into(); + let my_speed: Box = Box::new(S { s, t }); + + let p : *const S = Box::into_raw(my_speed); + let t = T(p); + + let c = || unsafe { + println!("{:?}", (*t.0).s); + }; + + c(); +} + +fn unsafe_mut() { + let s = "".into(); + let t = "".into(); + let mut my_speed: Box = Box::new(S { s, t }); + let p : *mut S = &mut *my_speed; + + let c = || { + let x = unsafe { &mut (*p).s }; + *x = "s".into(); + }; + c(); +} + +fn main() { + unsafe_mut(); + unsafe_imm(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr new file mode 100644 index 00000000000..c64c8b72e81 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr @@ -0,0 +1,11 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unsafe_ptr.rs:5:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs new file mode 100644 index 00000000000..79d3ecc2d2b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs @@ -0,0 +1,63 @@ +// Test that we restrict precision of a capture when we access a raw ptr, +// i.e. the capture doesn't deref the raw ptr. + +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 +#![feature(rustc_attrs)] + +#[derive(Debug)] +struct S { + s: String, + t: String, +} + +struct T(*const S); + +fn unsafe_imm() { + let s = "".into(); + let t = "".into(); + let my_speed: Box = Box::new(S { s, t }); + + let p : *const S = Box::into_raw(my_speed); + let t = T(p); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + || unsafe { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + println!("{:?}", (*t.0).s); + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow + }; + + c(); +} + +fn unsafe_mut() { + let s = "".into(); + let t = "".into(); + let mut my_speed: Box = Box::new(S { s, t }); + let p : *mut S = &mut *my_speed; + + 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 x = unsafe { &mut (*p).s }; + //~^ NOTE: Capturing p[Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture p[] -> ImmBorrow + *x = "s".into(); + }; + c(); +} + +fn main() { + unsafe_mut(); + unsafe_imm(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr new file mode 100644 index 00000000000..4508b2426e8 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr @@ -0,0 +1,102 @@ +error[E0658]: attributes on expressions are experimental + --> $DIR/unsafe_ptr.rs:26: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/unsafe_ptr.rs:46: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/unsafe_ptr.rs:4:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error: First Pass analysis includes: + --> $DIR/unsafe_ptr.rs:29:6 + | +LL | / || unsafe { +LL | | +LL | | +LL | | println!("{:?}", (*t.0).s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:32:26 + | +LL | println!("{:?}", (*t.0).s); + | ^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/unsafe_ptr.rs:29:6 + | +LL | / || unsafe { +LL | | +LL | | +LL | | println!("{:?}", (*t.0).s); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:32:26 + | +LL | println!("{:?}", (*t.0).s); + | ^^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/unsafe_ptr.rs:49:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = unsafe { &mut (*p).s }; +... | +LL | | *x = "s".into(); +LL | | }; + | |_____^ + | +note: Capturing p[Deref,(0, 0)] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:52:31 + | +LL | let x = unsafe { &mut (*p).s }; + | ^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/unsafe_ptr.rs:49:5 + | +LL | / || { +LL | | +LL | | +LL | | let x = unsafe { &mut (*p).s }; +... | +LL | | *x = "s".into(); +LL | | }; + | |_____^ + | +note: Min Capture p[] -> ImmBorrow + --> $DIR/unsafe_ptr.rs:52:31 + | +LL | let x = unsafe { &mut (*p).s }; + | ^^^^^^ + +error: aborting due to 6 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0658`.