Improve upvar analysis for deref of child capture

This commit is contained in:
Michael Goulet 2025-03-14 20:04:17 +00:00
parent ecade534c6
commit ae4a4794e7
4 changed files with 128 additions and 6 deletions

View File

@ -1862,8 +1862,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ///
/// (1.) Are we borrowing data owned by the parent closure? We can determine if /// (1.) Are we borrowing data owned by the parent closure? We can determine if
/// that is the case by checking if the parent capture is by move, EXCEPT if we /// that is the case by checking if the parent capture is by move, EXCEPT if we
/// apply a deref projection, which means we're reborrowing a reference that we /// apply a deref projection of an immutable reference, reborrows of immutable
/// captured by move. /// references which aren't restricted to the LUB of the lifetimes of the deref
/// chain. This is why `&'short mut &'long T` can be reborrowed as `&'long T`.
/// ///
/// ```rust /// ```rust
/// let x = &1i32; // Let's call this lifetime `'1`. /// let x = &1i32; // Let's call this lifetime `'1`.
@ -1902,10 +1903,22 @@ fn should_reborrow_from_env_of_parent_coroutine_closure<'tcx>(
) -> bool { ) -> bool {
// (1.) // (1.)
(!parent_capture.is_by_ref() (!parent_capture.is_by_ref()
&& !matches!( // This is just inlined `place.deref_tys()` but truncated to just
child_capture.place.projections.get(parent_capture.place.projections.len()), // the child projections. Namely, look for a `&T` deref, since we
Some(Projection { kind: ProjectionKind::Deref, .. }) // can always extend `&'short mut &'long T` to `&'long T`.
)) && !child_capture
.place
.projections
.iter()
.enumerate()
.skip(parent_capture.place.projections.len())
.any(|(idx, proj)| {
matches!(proj.kind, ProjectionKind::Deref)
&& matches!(
child_capture.place.ty_before_projection(idx).kind(),
ty::Ref(.., ty::Mutability::Not)
)
}))
// (2.) // (2.)
|| matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(ty::BorrowKind::Mutable)) || matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(ty::BorrowKind::Mutable))
} }

View File

@ -0,0 +1,46 @@
//@ edition: 2021
//@ check-pass
#![feature(impl_trait_in_bindings)]
struct FooS {
precise: i32,
}
fn ref_inside_mut(f: &mut &FooS) {
let x: impl AsyncFn() = async move || {
let y = &f.precise;
};
}
fn mut_inside_ref(f: &&mut FooS) {
let x: impl AsyncFn() = async move || {
let y = &f.precise;
};
}
fn mut_ref_inside_mut(f: &mut &mut FooS) {
let x: impl AsyncFn() = async move || {
let y = &f.precise;
};
}
fn ref_inside_box(f: Box<&FooS>) {
let x: impl AsyncFn() = async move || {
let y = &f.precise;
};
}
fn box_inside_ref(f: &Box<FooS>) {
let x: impl AsyncFn() = async move || {
let y = &f.precise;
};
}
fn box_inside_box(f: Box<Box<FooS>>) {
let x: impl AsyncFn() = async move || {
let y = &f.precise;
};
}
fn main() {}

View File

@ -0,0 +1,49 @@
//@ edition: 2021
#![feature(impl_trait_in_bindings)]
struct FooS {
precise: i32,
}
fn ref_inside_mut(f: &mut &FooS) {
let x: impl Fn() -> _ = async move || {
let y = &f.precise;
};
}
fn mut_inside_ref(f: &&mut FooS) {
let x: impl Fn() -> _ = async move || {
let y = &f.precise;
};
}
// Expected to fail, no immutable reference here.
fn mut_ref_inside_mut(f: &mut &mut FooS) {
let x: impl Fn() -> _ = async move || {
//~^ ERROR async closure does not implement `Fn`
let y = &f.precise;
};
}
fn ref_inside_box(f: Box<&FooS>) {
let x: impl Fn() -> _ = async move || {
let y = &f.precise;
};
}
fn box_inside_ref(f: &Box<FooS>) {
let x: impl Fn() -> _ = async move || {
let y = &f.precise;
};
}
// Expected to fail, no immutable reference here.
fn box_inside_box(f: Box<Box<FooS>>) {
let x: impl Fn() -> _ = async move || {
//~^ ERROR async closure does not implement `Fn`
let y = &f.precise;
};
}
fn main() {}

View File

@ -0,0 +1,14 @@
error: async closure does not implement `Fn` because it captures state from its environment
--> $DIR/imm-deref-not-lending.rs:23:29
|
LL | let x: impl Fn() -> _ = async move || {
| ^^^^^^^^^^^^^
error: async closure does not implement `Fn` because it captures state from its environment
--> $DIR/imm-deref-not-lending.rs:43:29
|
LL | let x: impl Fn() -> _ = async move || {
| ^^^^^^^^^^^^^
error: aborting due to 2 previous errors