mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
Improve upvar analysis for deref of child capture
This commit is contained in:
parent
ecade534c6
commit
ae4a4794e7
@ -1862,8 +1862,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
///
|
||||
/// (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
|
||||
/// apply a deref projection, which means we're reborrowing a reference that we
|
||||
/// captured by move.
|
||||
/// apply a deref projection of an immutable reference, reborrows of immutable
|
||||
/// 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
|
||||
/// let x = &1i32; // Let's call this lifetime `'1`.
|
||||
@ -1902,10 +1903,22 @@ fn should_reborrow_from_env_of_parent_coroutine_closure<'tcx>(
|
||||
) -> bool {
|
||||
// (1.)
|
||||
(!parent_capture.is_by_ref()
|
||||
&& !matches!(
|
||||
child_capture.place.projections.get(parent_capture.place.projections.len()),
|
||||
Some(Projection { kind: ProjectionKind::Deref, .. })
|
||||
))
|
||||
// This is just inlined `place.deref_tys()` but truncated to just
|
||||
// the child projections. Namely, look for a `&T` deref, since we
|
||||
// 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.)
|
||||
|| matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(ty::BorrowKind::Mutable))
|
||||
}
|
||||
|
46
tests/ui/async-await/async-closures/imm-deref-lending.rs
Normal file
46
tests/ui/async-await/async-closures/imm-deref-lending.rs
Normal 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() {}
|
49
tests/ui/async-await/async-closures/imm-deref-not-lending.rs
Normal file
49
tests/ui/async-await/async-closures/imm-deref-not-lending.rs
Normal 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() {}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user