"consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})",
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);
+ self.init_capture_info_for_place(&place_with_id, diag_expr_id);
}
- self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id, mode);
+ self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode);
}
fn borrow(
&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) {
}
}
+/// Reduces the precision of the captured place when the precision doesn't yeild any benefit from
+/// borrow checking prespective, allowing us to save us on the size of the capture.
+///
+///
+/// Fields that are read through a shared reference will always be read via a shared ref or a copy,
+/// and therefore capturing precise paths yields no benefit. This optimization truncates the
+/// rightmost deref of the capture if the deref is applied to a shared ref.
+///
+/// Reason we only drop the last deref is because of the following edge case:
+///
+/// ```rust
+/// struct MyStruct<'a> {
+/// a: &'static A,
+/// b: B,
+/// c: C<'a>,
+/// }
+///
+/// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+/// let c = || drop(&*m.a.field_of_a);
+/// // Here we really do want to capture `*m.a` because that outlives `'static`
+///
+/// // If we capture `m`, then the closure no longer outlives `'static'
+/// // it is constrained to `'a`
+/// }
+/// ```
+fn truncate_capture_for_optimization<'tcx>(place: &Place<'tcx>) -> Place<'tcx> {
+ let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not));
+
+ let idx = place.projections.iter().rposition(|proj| ProjectionKind::Deref == proj.kind);
+
+ match idx {
+ Some(idx) if is_shared_ref(place.ty_before_projection(idx)) => {
+ Place { projections: place.projections[0..=idx].to_vec(), ..place.clone() }
+ }
+ None | Some(_) => place.clone(),
+ }
+}
+
/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if
/// user is using Rust Edition 2021 or higher.
///
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
+ // `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
+ //~^ ERROR: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference
z.0.0.0 = format!("X1");
};
-error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
+error[E0596]: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference
--> $DIR/cant-mutate-imm-borrow.rs:13: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
+ | ------- mutable borrow occurs due to use of `*z.0.0` in closure
error: aborting due to previous error
//~^ 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
+ //~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow
+ //~| NOTE: Min Capture t[(0, 0),Deref] -> ImmBorrow
};
c();
//~^ 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),Deref] -> ImmBorrow
//~| NOTE: Capturing t[(0, 0)] -> ByValue
//~| NOTE: Min Capture t[(0, 0)] -> ByValue
};
LL | | };
| |_____^
|
-note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+note: Capturing t[(0, 0),Deref] -> ImmBorrow
--> $DIR/move_closure.rs:80:18
|
LL | let _t = t.0.0;
LL | | };
| |_____^
|
-note: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+note: Min Capture t[(0, 0),Deref] -> ImmBorrow
--> $DIR/move_closure.rs:80:18
|
LL | let _t = t.0.0;
LL | | };
| |_____^
|
-note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+note: Capturing t[(0, 0),Deref] -> ImmBorrow
--> $DIR/move_closure.rs:102:18
|
LL | let _t = t.0.0;
--- /dev/null
+#![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)]
+#![allow(unused)]
+#![allow(dead_code)]
+
+struct Int(i32);
+struct B<'a>(&'a i32);
+
+const I : Int = Int(0);
+const REF_I : &'static Int = &I;
+
+
+struct MyStruct<'a> {
+ a: &'static Int,
+ b: B<'a>,
+}
+
+fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+ let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ //~^ 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:
+ //~| NOTE: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow
+ //~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
+ c
+}
+
+fn main() {
+ let t = 0;
+ let s = MyStruct { a: REF_I, b: B(&t) };
+ let _ = foo(&s);
+}
--- /dev/null
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/edge_case.rs:23:13
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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/edge_case.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: First Pass analysis includes:
+ --> $DIR/edge_case.rs:23:39
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^^^^^^^^^^^
+ |
+note: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow
+ --> $DIR/edge_case.rs:23:48
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^
+
+error: Min Capture analysis includes:
+ --> $DIR/edge_case.rs:23:39
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^^^^^^^^^^^
+ |
+note: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
+ --> $DIR/edge_case.rs:23:48
+ |
+LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);
+ | ^^^^^
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#![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>
+
+// run-pass
+
+#![allow(unused)]
+#![allow(dead_code)]
+
+struct Int(i32);
+struct B<'a>(&'a i32);
+
+const I : Int = Int(0);
+const REF_I : &'static Int = &I;
+
+struct MyStruct<'a> {
+ a: &'static Int,
+ b: B<'a>,
+}
+
+fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+ let c = || drop(&m.a.0);
+ c
+}
+
+fn main() {
+ let t = 0;
+ let s = MyStruct { a: REF_I, b: B(&t) };
+ let _ = foo(&s);
+}
--- /dev/null
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/edge_case_run_pass.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
+
+warning: 1 warning emitted
+