Rollup merge of #82007 - sexxi-goose:reborrow, r=nikomatsakis
Implement reborrow for closure captures The strategy for captures is detailed here with examples: https://hackmd.io/PzxYMPY4RF-B9iH9uj9GTA Key points: - We only need to reborrow a capture in case of move closures. - If we mutate something via a `&mut` we store it as a `MutBorrow`/`UniqueMuBorrow` of the path containing the `&mut`, - Similarly, if it's read via `&` ref we just store it as a `ImmBorrow` of the path containing the `&` ref. - If a path doesn't deref a `&mut`, `&`, then that path is captured by Move. - If the use of a path results in a move when the closure is called, then that path is truncated before any deref and the truncated path is moved into the closure. - In the case of non-move closure if a use of a path results in a move, then the path is truncated before any deref and the truncated path is moved into the closure. Note that the implementation differs a bit from the document to allow for truncated path to be used in the ClosureKind analysis that happens as part of the first capture analysis pass. Closes: https://github.com/rust-lang/project-rfc-2229/issues/31 r? ````@nikomatsakis````
This commit is contained in:
commit
0b2f2b9413
8 changed files with 490 additions and 171 deletions
|
@ -180,7 +180,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
debug!("seed place {:?}", place);
|
||||
|
||||
let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id);
|
||||
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
|
||||
let capture_kind =
|
||||
self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span);
|
||||
let fake_info = ty::CaptureInfo {
|
||||
capture_kind_expr_id: None,
|
||||
path_expr_id: None,
|
||||
|
@ -205,11 +206,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// If we have an origin, store it.
|
||||
if let Some(origin) = delegate.current_origin.clone() {
|
||||
let origin = if self.tcx.features().capture_disjoint_fields {
|
||||
origin
|
||||
(origin.0, restrict_capture_precision(origin.1))
|
||||
} else {
|
||||
// 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 })
|
||||
};
|
||||
|
||||
|
@ -449,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
base => bug!("Expected upvar, found={:?}", base),
|
||||
};
|
||||
|
||||
let place = restrict_capture_precision(place, capture_info.capture_kind);
|
||||
let place = restrict_capture_precision(place);
|
||||
|
||||
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
|
||||
None => {
|
||||
|
@ -897,15 +895,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn init_capture_kind(
|
||||
fn init_capture_kind_for_place(
|
||||
&self,
|
||||
place: &Place<'tcx>,
|
||||
capture_clause: hir::CaptureBy,
|
||||
upvar_id: ty::UpvarId,
|
||||
closure_span: Span,
|
||||
) -> ty::UpvarCapture<'tcx> {
|
||||
match capture_clause {
|
||||
hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
|
||||
hir::CaptureBy::Ref => {
|
||||
// In case of a move closure if the data is accessed through a reference we
|
||||
// want to capture by ref to allow precise capture using reborrows.
|
||||
//
|
||||
// If the data will be moved out of this place, then the place will be truncated
|
||||
// at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
|
||||
// the closure.
|
||||
hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => {
|
||||
ty::UpvarCapture::ByValue(None)
|
||||
}
|
||||
hir::CaptureBy::Value | hir::CaptureBy::Ref => {
|
||||
let origin = UpvarRegion(upvar_id, closure_span);
|
||||
let upvar_region = self.next_region_var(origin);
|
||||
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
|
||||
|
@ -1109,12 +1116,25 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
place_with_id, diag_expr_id, mode
|
||||
);
|
||||
|
||||
// we only care about moves
|
||||
match mode {
|
||||
euv::Copy => {
|
||||
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;
|
||||
}
|
||||
euv::Move => {}
|
||||
|
||||
(hir::CaptureBy::Ref, euv::Move) | (hir::CaptureBy::Value, euv::Move | euv::Copy) => {}
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -1128,13 +1148,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
|
|||
|
||||
let usage_span = tcx.hir().span(diag_expr_id);
|
||||
|
||||
// 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_with_id.place.clone(),
|
||||
);
|
||||
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),
|
||||
|
@ -1317,8 +1339,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(self.capture_clause, upvar_id, self.closure_span);
|
||||
let capture_kind = self.fcx.init_capture_kind_for_place(
|
||||
&place_with_id.place,
|
||||
self.capture_clause,
|
||||
upvar_id,
|
||||
self.closure_span,
|
||||
);
|
||||
|
||||
let expr_id = Some(diag_expr_id);
|
||||
let capture_info = ty::CaptureInfo {
|
||||
|
@ -1392,15 +1418,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// 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> {
|
||||
fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
|
||||
if place.projections.is_empty() {
|
||||
// Nothing to do here
|
||||
return place;
|
||||
|
@ -1412,7 +1433,6 @@ fn restrict_capture_precision<'tcx>(
|
|||
}
|
||||
|
||||
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() {
|
||||
|
@ -1426,31 +1446,30 @@ fn restrict_capture_precision<'tcx>(
|
|||
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::Deref => {}
|
||||
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,
|
||||
});
|
||||
let length = place.projections.len().min(truncated_length);
|
||||
|
||||
place.projections.truncate(length);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(),
|
||||
|
|
|
@ -26,7 +26,8 @@ fn big_box() {
|
|||
//~^ First Pass analysis includes:
|
||||
//~| Min Capture analysis includes:
|
||||
let p = t.0.0;
|
||||
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
|
||||
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
//~| NOTE: Capturing t[(0, 0)] -> ByValue
|
||||
//~| NOTE: Min Capture t[(0, 0)] -> ByValue
|
||||
println!("{} {:?}", t.1, p);
|
||||
//~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow
|
||||
|
|
|
@ -28,13 +28,18 @@ LL | |
|
|||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
|
||||
note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
--> $DIR/by_value.rs:28:17
|
||||
|
|
||||
LL | let p = t.0.0;
|
||||
| ^^^^^
|
||||
note: Capturing t[(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
|
||||
--> $DIR/by_value.rs:32:29
|
||||
|
|
||||
LL | println!("{} {:?}", t.1, p);
|
||||
| ^^^
|
||||
|
@ -57,7 +62,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:31:29
|
||||
--> $DIR/by_value.rs:32:29
|
||||
|
|
||||
LL | println!("{} {:?}", t.1, p);
|
||||
| ^^^
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#![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>
|
||||
|
||||
// Test that array access is not stored as part of closure kind origin
|
||||
|
||||
fn expect_fn<F: Fn()>(_f: F) {}
|
||||
|
||||
fn main() {
|
||||
let s = [format!("s"), format!("s")];
|
||||
let c = || { //~ ERROR expected a closure that implements the `Fn`
|
||||
let [_, _s] = s;
|
||||
};
|
||||
expect_fn(c);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/closure-origin-array-diagnostics.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[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
|
||||
--> $DIR/closure-origin-array-diagnostics.rs:12:13
|
||||
|
|
||||
LL | let c = || {
|
||||
| ^^ this closure implements `FnOnce`, not `Fn`
|
||||
LL | let [_, _s] = s;
|
||||
| - closure is `FnOnce` because it moves the variable `s` out of its environment
|
||||
LL | };
|
||||
LL | expect_fn(c);
|
||||
| --------- the requirement to implement `Fn` derives from here
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0525`.
|
|
@ -6,48 +6,7 @@
|
|||
//~| 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() {
|
||||
fn simple_move_closure() {
|
||||
struct S(String);
|
||||
struct T(S);
|
||||
|
||||
|
@ -65,8 +24,118 @@ fn no_ref() {
|
|||
c();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
simple_ref();
|
||||
struct_contains_ref_to_another_struct();
|
||||
no_ref();
|
||||
// Test move closure use reborrows when using references
|
||||
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] -> UniqueImmBorrow
|
||||
//~| NOTE: Min Capture ref_s[Deref] -> UniqueImmBorrow
|
||||
};
|
||||
c();
|
||||
}
|
||||
|
||||
// Test move closure use reborrows when using references
|
||||
fn struct_contains_ref_to_another_struct_1() {
|
||||
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)] -> UniqueImmBorrow
|
||||
//~| NOTE: Min Capture t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
// Test that we can use reborrows to read data of Copy types
|
||||
// i.e. without truncating derefs
|
||||
fn struct_contains_ref_to_another_struct_2() {
|
||||
struct S(i32);
|
||||
struct T<'a>(&'a S);
|
||||
|
||||
let s = S(0);
|
||||
let t = T(&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:
|
||||
let _t = t.0.0;
|
||||
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
//~| NOTE: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
// Test that we can use truncate to move out of !Copy types
|
||||
fn struct_contains_ref_to_another_struct_3() {
|
||||
struct S(String);
|
||||
struct T<'a>(&'a S);
|
||||
|
||||
let s = S("s".into());
|
||||
let t = T(&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:
|
||||
let _t = t.0.0;
|
||||
//~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
//~| NOTE: Capturing t[(0, 0)] -> ByValue
|
||||
//~| NOTE: Min Capture t[(0, 0)] -> ByValue
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
// Test that derefs of box are truncated in move closures
|
||||
fn truncate_box_derefs() {
|
||||
struct S(i32);
|
||||
|
||||
let b = Box::new(S(10));
|
||||
|
||||
let 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:
|
||||
let _t = b.0;
|
||||
//~^ NOTE: Capturing b[Deref,(0, 0)] -> ByValue
|
||||
//~| NOTE: Capturing b[] -> ByValue
|
||||
//~| NOTE: Min Capture b[] -> ByValue
|
||||
};
|
||||
|
||||
c();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
simple_move_closure();
|
||||
simple_ref();
|
||||
struct_contains_ref_to_another_struct_1();
|
||||
struct_contains_ref_to_another_struct_2();
|
||||
struct_contains_ref_to_another_struct_3();
|
||||
truncate_box_derefs();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,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:35:17
|
||||
--> $DIR/move_closure.rs:32:17
|
||||
|
|
||||
LL | let mut c = #[rustc_capture_analysis]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -17,7 +17,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:55:17
|
||||
--> $DIR/move_closure.rs:53:17
|
||||
|
|
||||
LL | let mut c = #[rustc_capture_analysis]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -25,6 +25,33 @@ 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:76: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:98: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:119: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/move_closure.rs:3:12
|
||||
|
|
||||
|
@ -40,78 +67,6 @@ error: First Pass analysis includes:
|
|||
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 | |
|
||||
|
@ -119,13 +74,13 @@ LL | | };
|
|||
| |_____^
|
||||
|
|
||||
note: Capturing t[(0, 0),(0, 0)] -> ByValue
|
||||
--> $DIR/move_closure.rs:61:9
|
||||
--> $DIR/move_closure.rs:20:9
|
||||
|
|
||||
LL | t.0.0 = "new S".into();
|
||||
| ^^^^^
|
||||
|
||||
error: Min Capture analysis includes:
|
||||
--> $DIR/move_closure.rs:58:5
|
||||
--> $DIR/move_closure.rs:17:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
|
@ -137,11 +92,201 @@ LL | | };
|
|||
| |_____^
|
||||
|
|
||||
note: Min Capture t[(0, 0),(0, 0)] -> ByValue
|
||||
--> $DIR/move_closure.rs:61:9
|
||||
--> $DIR/move_closure.rs:20:9
|
||||
|
|
||||
LL | t.0.0 = "new S".into();
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 9 previous errors; 1 warning emitted
|
||||
error: First Pass analysis includes:
|
||||
--> $DIR/move_closure.rs:35:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | *ref_s += 10;
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Capturing ref_s[Deref] -> UniqueImmBorrow
|
||||
--> $DIR/move_closure.rs:38:9
|
||||
|
|
||||
LL | *ref_s += 10;
|
||||
| ^^^^^^
|
||||
|
||||
error: Min Capture analysis includes:
|
||||
--> $DIR/move_closure.rs:35:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | *ref_s += 10;
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Min Capture ref_s[Deref] -> UniqueImmBorrow
|
||||
--> $DIR/move_closure.rs:38:9
|
||||
|
|
||||
LL | *ref_s += 10;
|
||||
| ^^^^^^
|
||||
|
||||
error: First Pass analysis includes:
|
||||
--> $DIR/move_closure.rs:56:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | t.0.0 = "new s".into();
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Capturing t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow
|
||||
--> $DIR/move_closure.rs:59:9
|
||||
|
|
||||
LL | t.0.0 = "new s".into();
|
||||
| ^^^^^
|
||||
|
||||
error: Min Capture analysis includes:
|
||||
--> $DIR/move_closure.rs:56:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | t.0.0 = "new s".into();
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Min Capture t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow
|
||||
--> $DIR/move_closure.rs:59:9
|
||||
|
|
||||
LL | t.0.0 = "new s".into();
|
||||
| ^^^^^
|
||||
|
||||
error: First Pass analysis includes:
|
||||
--> $DIR/move_closure.rs:79:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | let _t = t.0.0;
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
--> $DIR/move_closure.rs:82:18
|
||||
|
|
||||
LL | let _t = t.0.0;
|
||||
| ^^^^^
|
||||
|
||||
error: Min Capture analysis includes:
|
||||
--> $DIR/move_closure.rs:79:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | let _t = t.0.0;
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
--> $DIR/move_closure.rs:82:18
|
||||
|
|
||||
LL | let _t = t.0.0;
|
||||
| ^^^^^
|
||||
|
||||
error: First Pass analysis includes:
|
||||
--> $DIR/move_closure.rs:101:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | let _t = t.0.0;
|
||||
... |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
|
||||
--> $DIR/move_closure.rs:104:18
|
||||
|
|
||||
LL | let _t = t.0.0;
|
||||
| ^^^^^
|
||||
note: Capturing t[(0, 0)] -> ByValue
|
||||
--> $DIR/move_closure.rs:104:18
|
||||
|
|
||||
LL | let _t = t.0.0;
|
||||
| ^^^^^
|
||||
|
||||
error: Min Capture analysis includes:
|
||||
--> $DIR/move_closure.rs:101:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | let _t = t.0.0;
|
||||
... |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Min Capture t[(0, 0)] -> ByValue
|
||||
--> $DIR/move_closure.rs:104:18
|
||||
|
|
||||
LL | let _t = t.0.0;
|
||||
| ^^^^^
|
||||
|
||||
error: First Pass analysis includes:
|
||||
--> $DIR/move_closure.rs:122:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | let _t = b.0;
|
||||
... |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Capturing b[Deref,(0, 0)] -> ByValue
|
||||
--> $DIR/move_closure.rs:125:18
|
||||
|
|
||||
LL | let _t = b.0;
|
||||
| ^^^
|
||||
note: Capturing b[] -> ByValue
|
||||
--> $DIR/move_closure.rs:125:18
|
||||
|
|
||||
LL | let _t = b.0;
|
||||
| ^^^
|
||||
|
||||
error: Min Capture analysis includes:
|
||||
--> $DIR/move_closure.rs:122:5
|
||||
|
|
||||
LL | / move || {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | let _t = b.0;
|
||||
... |
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____^
|
||||
|
|
||||
note: Min Capture b[] -> ByValue
|
||||
--> $DIR/move_closure.rs:125:18
|
||||
|
|
||||
LL | let _t = b.0;
|
||||
| ^^^
|
||||
|
||||
error: aborting due to 18 previous errors; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
|
|
@ -56,9 +56,50 @@ fn no_ref_nested() {
|
|||
c();
|
||||
}
|
||||
|
||||
struct A<'a>(&'a mut String, &'a mut String);
|
||||
// Test that reborrowing works as expected for move closures
|
||||
// by attempting a disjoint capture through a reference.
|
||||
fn disjoint_via_ref() {
|
||||
let mut x = String::new();
|
||||
let mut y = String::new();
|
||||
|
||||
let mut a = A(&mut x, &mut y);
|
||||
let a = &mut a;
|
||||
|
||||
let mut c1 = move || {
|
||||
a.0.truncate(0);
|
||||
};
|
||||
|
||||
let mut c2 = move || {
|
||||
a.1.truncate(0);
|
||||
};
|
||||
|
||||
c1();
|
||||
c2();
|
||||
}
|
||||
|
||||
// Test that even if a path is moved into the closure, the closure is not FnOnce
|
||||
// if the path is not moved by the closure call.
|
||||
fn data_moved_but_not_fn_once() {
|
||||
let x = Box::new(10i32);
|
||||
|
||||
let c = move || {
|
||||
// *x has type i32 which is Copy. So even though the box `x` will be moved
|
||||
// into the closure, `x` is never moved when the closure is called, i.e. the
|
||||
// ownership stays with the closure and therefore we can call the function multiple times.
|
||||
let _x = *x;
|
||||
};
|
||||
|
||||
c();
|
||||
c();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
simple_ref();
|
||||
struct_contains_ref_to_another_struct();
|
||||
no_ref();
|
||||
no_ref_nested();
|
||||
|
||||
disjoint_via_ref();
|
||||
data_moved_but_not_fn_once();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue