Rollup merge of #80092 - sexxi-goose:restrict_precision, r=nikomatsakis
2229: Fix issues with move closures and mutability This PR fixes two issues when feature `capture_disjoint_fields` is used. 1. Can't mutate using a mutable reference 2. Move closures try to move value out through a reference. To do so, we 1. Compute the mutability of the capture and store it as part of the `CapturedPlace` that is written in TypeckResults 2. Restrict capture precision. Note this is temporary for now, to allow the feature to be used with move closures and ByValue captures and might change depending on discussions with the lang team. - No Derefs are captured for ByValue captures, since that 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. r? `````@nikomatsakis`````
This commit is contained in:
commit
7e3a8ec688
@ -661,11 +661,28 @@ pub struct UpvarBorrow<'tcx> {
|
||||
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
|
||||
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
|
||||
|
||||
/// 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 {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
local_names: &IndexVec<Local, Option<Symbol>>,
|
||||
upvars: &[Upvar],
|
||||
upvars: &[Upvar<'tcx>],
|
||||
fr: RegionVid,
|
||||
) -> Option<(Option<Symbol>, 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);
|
||||
|
@ -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<BasicBlock>,
|
||||
|
||||
/// Information about upvars not necessarily preserved in types or MIR
|
||||
upvars: Vec<Upvar>,
|
||||
upvars: Vec<Upvar<'tcx>>,
|
||||
|
||||
/// Names of local (user) variables (extracted from `var_debug_info`).
|
||||
local_names: IndexVec<Local, Option<Symbol>>,
|
||||
@ -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<RootPlace<'tcx>, 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
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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<Field> {
|
||||
|
@ -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<RegionValueElements>,
|
||||
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<AllFacts>,
|
||||
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
|
||||
};
|
||||
|
@ -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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Ty<'tcx>> {
|
||||
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(),
|
||||
|
41
src/test/ui/closures/2229_closure_analysis/by_value.rs
Normal file
41
src/test/ui/closures/2229_closure_analysis/by_value.rs
Normal file
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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();
|
||||
}
|
67
src/test/ui/closures/2229_closure_analysis/by_value.stderr
Normal file
67
src/test/ui/closures/2229_closure_analysis/by_value.stderr
Normal file
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/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`.
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/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`.
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/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`.
|
72
src/test/ui/closures/2229_closure_analysis/move_closure.rs
Normal file
72
src/test/ui/closures/2229_closure_analysis/move_closure.rs
Normal file
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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();
|
||||
}
|
147
src/test/ui/closures/2229_closure_analysis/move_closure.stderr
Normal file
147
src/test/ui/closures/2229_closure_analysis/move_closure.stderr
Normal file
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/53488> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/53488> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/53488> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/53488> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -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<S> = 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<S> = 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();
|
||||
}
|
@ -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 <https://github.com/rust-lang/rust/issues/53488> for more information
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
63
src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
Normal file
63
src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
Normal file
@ -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 <https://github.com/rust-lang/rust/issues/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<S> = 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 <https://github.com/rust-lang/rust/issues/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<S> = 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 <https://github.com/rust-lang/rust/issues/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();
|
||||
}
|
102
src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
Normal file
102
src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
Normal file
@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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`.
|
Loading…
Reference in New Issue
Block a user