Auto merge of #86995 - sexxi-goose:rewrite, r=nikomatsakis

2229: Rewrite/Refactor Closure Capture Analaysis

While handling all the differnet edge cases the code for the captur analysis got pretty compicated. Looking at the overall picture of the edge cases the rules can still be layed out simply.

Alogithm: https://hackmd.io/D3I_gwvuT-SPnJ22tgJumw

r? `@nikomatsakis`

Closes https://github.com/rust-lang/project-rfc-2229/issues/52
Implementation part of https://github.com/rust-lang/project-rfc-2229/issues/53
This commit is contained in:
bors 2021-07-11 11:25:31 +00:00
commit 81053b912f
7 changed files with 303 additions and 225 deletions

View File

@ -165,9 +165,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fcx: self,
closure_def_id,
closure_span: span,
capture_clause,
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
current_origin: None,
capture_information: Default::default(),
fake_reads: Default::default(),
};
@ -184,9 +181,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"For closure={:?}, capture_information={:#?}",
closure_def_id, delegate.capture_information
);
self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
self.compute_min_captures(closure_def_id, capture_clause, delegate.capture_information);
let (capture_information, closure_kind, origin) = self
.process_collected_capture_information(capture_clause, delegate.capture_information);
self.compute_min_captures(closure_def_id, capture_information);
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
@ -221,7 +222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// This will update the min captures based on this new fake information.
self.compute_min_captures(closure_def_id, capture_clause, capture_information);
self.compute_min_captures(closure_def_id, capture_information);
}
let before_feature_tys = self.final_upvar_tys(closure_def_id);
@ -229,14 +230,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(closure_substs) = infer_kind {
// Unify the (as yet unbound) type variable in the closure
// substs with the kind we inferred.
let inferred_kind = delegate.current_closure_kind;
let closure_kind_ty = closure_substs.as_closure().kind_ty();
self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty);
self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty);
// If we have an origin, store it.
if let Some(origin) = delegate.current_origin.clone() {
if let Some(origin) = origin {
let origin = if enable_precise_capture(self.tcx, span) {
(origin.0, restrict_capture_precision(capture_clause, origin.1))
(origin.0, origin.1)
} else {
(origin.0, Place { projections: vec![], ..origin.1 })
};
@ -323,6 +323,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect()
}
/// Adjusts the closure capture information to ensure that the operations aren't unsafe,
/// and that the path can be captured with required capture kind (depending on use in closure,
/// move closure etc.)
///
/// Returns the set of of adjusted information along with the inferred closure kind and span
/// associated with the closure kind inference.
///
/// Note that we *always* infer a minimal kind, even if
/// we don't always *use* that in the final result (i.e., sometimes
/// we've taken the closure kind from the expectations instead, and
/// for generators we don't even implement the closure traits
/// really).
///
/// If we inferred that the closure needs to be FnMut/FnOnce, last element of the returned tuple
/// contains a `Some()` with the `Place` that caused us to do so.
fn process_collected_capture_information(
&self,
capture_clause: hir::CaptureBy,
capture_information: InferredCaptureInformation<'tcx>,
) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) {
let mut processed: InferredCaptureInformation<'tcx> = Default::default();
let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM;
let mut origin: Option<(Span, Place<'tcx>)> = None;
for (place, mut capture_info) in capture_information {
// Apply rules for safety before inferring closure kind
let place = restrict_capture_precision(place);
let place = truncate_capture_for_optimization(&place);
let usage_span = if let Some(usage_expr) = capture_info.path_expr_id {
self.tcx.hir().span(usage_expr)
} else {
unreachable!()
};
let updated = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(..) => match closure_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
(ty::ClosureKind::FnOnce, Some((usage_span, place.clone())))
}
// If closure is already FnOnce, don't update
ty::ClosureKind::FnOnce => (closure_kind, origin),
},
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
..
}) => {
match closure_kind {
ty::ClosureKind::Fn => {
(ty::ClosureKind::FnMut, Some((usage_span, place.clone())))
}
// Don't update the origin
ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => (closure_kind, origin),
}
}
_ => (closure_kind, origin),
};
closure_kind = updated.0;
origin = updated.1;
let (place, capture_kind) = match capture_clause {
hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind),
hir::CaptureBy::Ref => {
adjust_for_non_move_closure(place, capture_info.capture_kind)
}
};
capture_info.capture_kind = capture_kind;
processed.insert(place, capture_info);
}
(processed, closure_kind, origin)
}
/// Analyzes the information collected by `InferBorrowKind` to compute the min number of
/// Places (and corresponding capture kind) that we need to keep track of to support all
/// the required captured paths.
@ -395,7 +474,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn compute_min_captures(
&self,
closure_def_id: DefId,
capture_clause: hir::CaptureBy,
capture_information: InferredCaptureInformation<'tcx>,
) {
if capture_information.is_empty() {
@ -413,8 +491,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base => bug!("Expected upvar, found={:?}", base),
};
let place = restrict_capture_precision(capture_clause, place);
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
None => {
let mutability = self.determine_capture_mutability(&typeck_results, &place);
@ -1417,20 +1493,6 @@ struct InferBorrowKind<'a, 'tcx> {
closure_span: Span,
capture_clause: hir::CaptureBy,
// The kind that we have inferred that the current closure
// requires. Note that we *always* infer a minimal kind, even if
// we don't always *use* that in the final result (i.e., sometimes
// we've taken the closure kind from the expectations instead, and
// for generators we don't even implement the closure traits
// really).
current_closure_kind: ty::ClosureKind,
// If we modified `current_closure_kind`, this field contains a `Some()` with the
// variable access that caused us to do so.
current_origin: Option<(Span, Place<'tcx>)>,
/// For each Place that is captured by the closure, we track the minimal kind of
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
///
@ -1473,27 +1535,13 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
place_with_id, diag_expr_id, mode
);
match (self.capture_clause, mode) {
// In non-move closures, we only care about moves
(hir::CaptureBy::Ref, euv::Copy) => return,
// We want to capture Copy types that read through a ref via a reborrow
(hir::CaptureBy::Value, euv::Copy)
if place_with_id.place.deref_tys().any(ty::TyS::is_ref) =>
{
return;
}
(hir::CaptureBy::Ref, euv::Move) | (hir::CaptureBy::Value, euv::Move | euv::Copy) => {}
// Copy type being used as ByValue are equivalent to ImmBorrow and don't require any
// escalation.
match mode {
euv::ConsumeMode::Copy => return,
euv::ConsumeMode::Move => {}
};
let place = truncate_capture_for_move(place_with_id.place.clone());
let place_with_id = PlaceWithHirId { place: place.clone(), hir_id: place_with_id.hir_id };
if !self.capture_information.contains_key(&place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
}
let tcx = self.fcx.tcx;
let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
upvar_id
@ -1505,16 +1553,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
let usage_span = tcx.hir().span(diag_expr_id);
if matches!(mode, euv::Move) {
// To move out of an upvar, this must be a FnOnce closure
self.adjust_closure_kind(
upvar_id.closure_expr_id,
ty::ClosureKind::FnOnce,
usage_span,
place.clone(),
);
}
let capture_info = ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
@ -1592,22 +1630,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
ty::ImmBorrow => false,
});
let tcx = self.fcx.tcx;
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary
self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind);
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
self.adjust_closure_kind(
upvar_id.closure_expr_id,
ty::ClosureKind::FnMut,
tcx.hir().span(diag_expr_id),
place_with_id.place.clone(),
);
}
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure.
@ -1646,48 +1673,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
};
}
fn adjust_closure_kind(
&mut self,
closure_id: LocalDefId,
new_kind: ty::ClosureKind,
upvar_span: Span,
place: Place<'tcx>,
) {
debug!(
"adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})",
closure_id, new_kind, upvar_span, place
);
// Is this the closure whose kind is currently being inferred?
if closure_id.to_def_id() != self.closure_def_id {
debug!("adjust_closure_kind: not current closure");
return;
}
// closures start out as `Fn`.
let existing_kind = self.current_closure_kind;
debug!(
"adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}",
closure_id, existing_kind, new_kind
);
match (existing_kind, new_kind) {
(ty::ClosureKind::Fn, ty::ClosureKind::Fn)
| (ty::ClosureKind::FnMut, ty::ClosureKind::Fn | ty::ClosureKind::FnMut)
| (ty::ClosureKind::FnOnce, _) => {
// no change needed
}
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce)
| (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
// new kind is stronger than the old kind
self.current_closure_kind = new_kind;
self.current_origin = Some((upvar_span, place));
}
}
}
fn init_capture_info_for_place(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
@ -1696,12 +1681,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id);
let capture_kind = self.fcx.init_capture_kind_for_place(
&place_with_id.place,
self.capture_clause,
upvar_id,
self.closure_span,
);
// Initialize to ImmBorrow
// We will escalate the CaptureKind based on any uses we see or in `process_collected_capture_information`.
let origin = UpvarRegion(upvar_id, self.closure_span);
let upvar_region = self.fcx.next_region_var(origin);
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
let capture_kind = ty::UpvarCapture::ByRef(upvar_borrow);
let expr_id = Some(diag_expr_id);
let capture_info = ty::CaptureInfo {
@ -1724,7 +1709,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
if let PlaceBase::Upvar(_) = place.base {
// We need to restrict Fake Read precision to avoid fake reading unsafe code,
// such as deref of a raw pointer.
let place = restrict_capture_precision(self.capture_clause, place);
let place = restrict_capture_precision(place);
let place =
restrict_repr_packed_field_ref_capture(self.fcx.tcx, self.fcx.param_env, &place);
self.fake_reads.push((place, cause, diag_expr_id));
@ -1742,11 +1727,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
place_with_id, diag_expr_id, mode
);
let place_with_id = PlaceWithHirId {
place: truncate_capture_for_optimization(&place_with_id.place),
..*place_with_id
};
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
}
@ -1766,7 +1746,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
);
// We only want repr packed restriction to be applied to reading references into a packed
// struct, and not when the data is being moved. There for we call this method here instead
// struct, and not when the data is being moved. Therefore we call this method here instead
// of in `restrict_capture_precision`.
let place = restrict_repr_packed_field_ref_capture(
self.fcx.tcx,
@ -1774,8 +1754,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
&place_with_id.place,
);
let place = truncate_capture_for_optimization(&place);
let place_with_id = PlaceWithHirId { place, ..*place_with_id };
if !self.capture_information.contains_key(&place_with_id.place) {
@ -1800,46 +1778,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
}
}
/// Deref of a box isn't captured in move clousres. This is motivated by:
/// 1. We only want to capture data that is on the stack
/// 2. One motivation for the user to use a box might be to reduce the amount of data that gets
/// moved (if size of pointer < size of data). We want to make sure that this optimization that
/// the user made is respected.
fn restrict_precision_for_box<'tcx>(
capture_clause: hir::CaptureBy,
mut place: Place<'tcx>,
) -> Place<'tcx> {
match capture_clause {
hir::CaptureBy::Ref => {}
hir::CaptureBy::Value => {
if ty::TyS::is_box(place.base_ty) {
place.projections.truncate(0);
} else {
// Either the box is the last access or there is a deref applied on the box
// In either case we want to stop at the box.
let pos = place.projections.iter().position(|proj| ty::TyS::is_box(proj.ty));
match pos {
None => {}
Some(idx) => {
place.projections.truncate(idx + 1);
}
}
}
}
}
place
}
/// Truncate projections so that following rules are obeyed by the captured `place`:
/// - 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.
/// - Deref of a box isn't captured in move clousres.
fn restrict_capture_precision<'tcx>(
capture_clause: hir::CaptureBy,
mut place: Place<'tcx>,
) -> Place<'tcx> {
fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
if place.projections.is_empty() {
// Nothing to do here
return place;
@ -1874,19 +1817,68 @@ fn restrict_capture_precision<'tcx>(
place.projections.truncate(length);
// Dont't capture projections on top of a box in move closures.
restrict_precision_for_box(capture_clause, place)
place
}
/// Truncates a place so that the resultant capture doesn't move data out of a reference
fn truncate_capture_for_move(mut place: Place<'tcx>) -> Place<'tcx> {
if let Some(i) = place.projections.iter().position(|proj| proj.kind == 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.
place.projections.truncate(i);
}
/// Take ownership if data being accessed is owned by the variable used to access it
/// (or if closure attempts to move data that it doesnt own).
/// Note: When taking ownership, only capture data found on the stack.
fn adjust_for_move_closure<'tcx>(
mut place: Place<'tcx>,
kind: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
let contains_deref_of_ref = place.deref_tys().any(|ty| ty.is_ref());
let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
place
match kind {
ty::UpvarCapture::ByRef(..) if contains_deref_of_ref => (place, kind),
// If there's any Deref and the data needs to be moved into the closure body,
// or it's a Deref of a Box, truncate the path to the first deref
_ if first_deref.is_some() => {
let place = match first_deref {
Some(idx) => {
place.projections.truncate(idx);
place
}
None => place,
};
// AMAN: I think we don't need the span inside the ByValue anymore
// we have more detailed span in CaptureInfo
(place, ty::UpvarCapture::ByValue(None))
}
_ => (place, ty::UpvarCapture::ByValue(None)),
}
}
/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
fn adjust_for_non_move_closure<'tcx>(
mut place: Place<'tcx>,
kind: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
let contains_deref =
place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
match kind {
ty::UpvarCapture::ByValue(..) if contains_deref.is_some() => {
let place = match contains_deref {
Some(idx) => {
place.projections.truncate(idx);
place
}
// Because of the if guard on the match on `kind`, we should never get here.
None => unreachable!(),
};
(place, kind)
}
ty::UpvarCapture::ByValue(..) => (place, kind),
ty::UpvarCapture::ByRef(..) => (place, kind),
}
}
fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {

View File

@ -22,8 +22,7 @@ fn big_box() {
//~^ First Pass analysis includes:
//~| Min Capture analysis includes:
let p = t.0.0;
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Capturing t[(0, 0)] -> ByValue
//~^ 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

View File

@ -19,18 +19,13 @@ LL | |
LL | | };
| |_____^
|
note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
--> $DIR/by_value.rs:24:17
|
LL | let p = t.0.0;
| ^^^^^
note: Capturing t[(0, 0)] -> ByValue
note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
--> $DIR/by_value.rs:24:17
|
LL | let p = t.0.0;
| ^^^^^
note: Capturing t[(1, 0)] -> ImmBorrow
--> $DIR/by_value.rs:28:29
--> $DIR/by_value.rs:27:29
|
LL | println!("{} {:?}", t.1, p);
| ^^^
@ -53,7 +48,7 @@ note: Min Capture t[(0, 0)] -> ByValue
LL | let p = t.0.0;
| ^^^^^
note: Min Capture t[(1, 0)] -> ImmBorrow
--> $DIR/by_value.rs:28:29
--> $DIR/by_value.rs:27:29
|
LL | println!("{} {:?}", t.1, p);
| ^^^

View File

@ -16,7 +16,7 @@ fn simple_move_closure() {
//~^ 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: Capturing t[(0, 0),(0, 0)] -> MutBorrow
//~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue
};
c();
@ -78,7 +78,7 @@ fn struct_contains_ref_to_another_struct_2() {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let _t = t.0.0;
//~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture t[(0, 0),Deref] -> ImmBorrow
};
@ -100,8 +100,7 @@ fn struct_contains_ref_to_another_struct_3() {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let _t = t.0.0;
//~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow
//~| NOTE: Capturing t[(0, 0)] -> ByValue
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
//~| NOTE: Min Capture t[(0, 0)] -> ByValue
};
@ -122,8 +121,7 @@ fn truncate_box_derefs() {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let _t = b.0;
//~^ NOTE: Capturing b[Deref,(0, 0)] -> ByValue
//~| NOTE: Capturing b[] -> ByValue
//~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture b[] -> ByValue
};
@ -139,7 +137,7 @@ fn truncate_box_derefs() {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", b.0);
//~^ NOTE: Capturing b[Deref,(0, 0)] -> ByValue
//~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture b[] -> ByValue
};
@ -156,11 +154,47 @@ fn truncate_box_derefs() {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", t.1.0);
//~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ByValue
//~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture t[(1, 0)] -> ByValue
};
}
struct Foo { x: i32 }
// Ensure that even in move closures, if the data is not owned by the root variable
// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
fn box_mut_1() {
let mut foo = Foo { x: 0 } ;
let p_foo = &mut foo;
let box_p_foo = Box::new(p_foo);
let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
//~| First Pass analysis includes:
//~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
//~| Min Capture analysis includes:
//~| NOTE: Min Capture box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
}
// Ensure that even in move closures, if the data is not owned by the root variable
// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
fn box_mut_2() {
let foo = Foo { x: 0 } ;
let mut box_foo = Box::new(foo);
let p_foo = &mut box_foo;
let c = #[rustc_capture_analysis] move || p_foo.x += 10;
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
//~| First Pass analysis includes:
//~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
//~| Min Capture analysis includes:
//~| NOTE: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
}
fn main() {
simple_move_closure();
simple_ref();
@ -168,4 +202,6 @@ fn main() {
struct_contains_ref_to_another_struct_2();
struct_contains_ref_to_another_struct_3();
truncate_box_derefs();
box_mut_2();
box_mut_1();
}

View File

@ -44,7 +44,7 @@ LL | let mut c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/move_closure.rs:118:13
--> $DIR/move_closure.rs:117:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -53,7 +53,7 @@ LL | let c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/move_closure.rs:135:13
--> $DIR/move_closure.rs:133:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -62,7 +62,7 @@ LL | let c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/move_closure.rs:152:13
--> $DIR/move_closure.rs:150:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -70,6 +70,24 @@ 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/move_closure.rs:172:13
|
LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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:189:13
|
LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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: First Pass analysis includes:
--> $DIR/move_closure.rs:15:5
|
@ -82,7 +100,7 @@ LL | |
LL | | };
| |_____^
|
note: Capturing t[(0, 0),(0, 0)] -> ByValue
note: Capturing t[(0, 0),(0, 0)] -> MutBorrow
--> $DIR/move_closure.rs:18:9
|
LL | t.0.0 = "new S".into();
@ -190,7 +208,7 @@ LL | |
LL | | };
| |_____^
|
note: Capturing t[(0, 0),Deref] -> ImmBorrow
note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
--> $DIR/move_closure.rs:80:18
|
LL | let _t = t.0.0;
@ -221,17 +239,12 @@ LL | / move || {
LL | |
LL | |
LL | | let _t = t.0.0;
... |
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing t[(0, 0),Deref] -> ImmBorrow
--> $DIR/move_closure.rs:102:18
|
LL | let _t = t.0.0;
| ^^^^^
note: Capturing t[(0, 0)] -> ByValue
note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:102:18
|
LL | let _t = t.0.0;
@ -244,7 +257,7 @@ LL | / move || {
LL | |
LL | |
LL | | let _t = t.0.0;
... |
LL | |
LL | |
LL | | };
| |_____^
@ -256,48 +269,43 @@ LL | let _t = t.0.0;
| ^^^^^
error: First Pass analysis includes:
--> $DIR/move_closure.rs:121:5
--> $DIR/move_closure.rs:120:5
|
LL | / move || {
LL | |
LL | |
LL | | let _t = b.0;
... |
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing b[Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:124:18
|
LL | let _t = b.0;
| ^^^
note: Capturing b[] -> ByValue
--> $DIR/move_closure.rs:124:18
note: Capturing b[Deref,(0, 0)] -> ImmBorrow
--> $DIR/move_closure.rs:123:18
|
LL | let _t = b.0;
| ^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:121:5
--> $DIR/move_closure.rs:120:5
|
LL | / move || {
LL | |
LL | |
LL | | let _t = b.0;
... |
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture b[] -> ByValue
--> $DIR/move_closure.rs:124:18
--> $DIR/move_closure.rs:123:18
|
LL | let _t = b.0;
| ^^^
error: First Pass analysis includes:
--> $DIR/move_closure.rs:138:5
--> $DIR/move_closure.rs:136:5
|
LL | / move || {
LL | |
@ -308,14 +316,14 @@ LL | |
LL | | };
| |_____^
|
note: Capturing b[Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:141:24
note: Capturing b[Deref,(0, 0)] -> ImmBorrow
--> $DIR/move_closure.rs:139:24
|
LL | println!("{}", b.0);
| ^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:138:5
--> $DIR/move_closure.rs:136:5
|
LL | / move || {
LL | |
@ -327,13 +335,13 @@ LL | | };
| |_____^
|
note: Min Capture b[] -> ByValue
--> $DIR/move_closure.rs:141:24
--> $DIR/move_closure.rs:139:24
|
LL | println!("{}", b.0);
| ^^^
error: First Pass analysis includes:
--> $DIR/move_closure.rs:155:5
--> $DIR/move_closure.rs:153:5
|
LL | / move || {
LL | |
@ -344,14 +352,14 @@ LL | |
LL | | };
| |_____^
|
note: Capturing t[(1, 0),Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:158:24
note: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
--> $DIR/move_closure.rs:156:24
|
LL | println!("{}", t.1.0);
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:155:5
--> $DIR/move_closure.rs:153:5
|
LL | / move || {
LL | |
@ -363,11 +371,59 @@ LL | | };
| |_____^
|
note: Min Capture t[(1, 0)] -> ByValue
--> $DIR/move_closure.rs:158:24
--> $DIR/move_closure.rs:156:24
|
LL | println!("{}", t.1.0);
| ^^^^^
error: aborting due to 24 previous errors
error: First Pass analysis includes:
--> $DIR/move_closure.rs:172:39
|
LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: Capturing box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
--> $DIR/move_closure.rs:172:47
|
LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
| ^^^^^^^^^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:172:39
|
LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: Min Capture box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
--> $DIR/move_closure.rs:172:47
|
LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
| ^^^^^^^^^^^
error: First Pass analysis includes:
--> $DIR/move_closure.rs:189:39
|
LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: Capturing p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
--> $DIR/move_closure.rs:189:47
|
LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
| ^^^^^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:189:39
|
LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow
--> $DIR/move_closure.rs:189:47
|
LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10;
| ^^^^^^^
error: aborting due to 30 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -22,7 +22,7 @@ fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
//~| ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
//~| NOTE: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow
//~| NOTE: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow
//~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
c
}

View File

@ -13,7 +13,7 @@ error: First Pass analysis includes:
LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
| ^^^^^^^^^^^^^^^
|
note: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow
note: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow
--> $DIR/edge_case.rs:20:48
|
LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);