Skip to content

Commit

Permalink
Rollup merge of #123350 - compiler-errors:async-closure-by-move, r=ol…
Browse files Browse the repository at this point in the history
…i-obk

Actually use the inferred `ClosureKind` from signature inference in coroutine-closures

A follow-up to rust-lang/rust#123349, which fixes another subtle bug: We were not taking into account the async closure kind we infer during closure signature inference.

When I pass a closure directly to an arg like `fn(x: impl async FnOnce())`, that should have the side-effect of artificially restricting the kind of the async closure to `ClosureKind::FnOnce`. We weren't doing this -- that's a quick fix; however, it uncovers a second, more subtle bug with the way that `move`, async closures, and `FnOnce` interact.

Specifically, when we have an async closure like:
```
let x = Struct;
let c = infer_as_fnonce(async move || {
  println!("{x:?}");
}
```

The outer closure captures `x` by move, but the inner coroutine still immutably borrows `x` from the outer closure. Since we've forced the closure to by `async FnOnce()`, we can't actually *do* a self borrow, since the signature of `AsyncFnOnce::call_once` doesn't have a borrowed lifetime. This means that all `async move` closures that are constrained to `FnOnce` will fail borrowck.

We can fix that by detecting this case specifically, and making the *inner* async closure `move` as well. This is always beneficial to closure analysis, since if we have an `async FnOnce()` that's `move`, there's no reason to ever borrow anything, so `move` isn't artificially restrictive.
  • Loading branch information
GuillaumeGomez authored Apr 5, 2024
2 parents 35b90a6 + 35f0eca commit 5d6f914
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 0 deletions.
34 changes: 34 additions & 0 deletions tests/pass/async-closure-captures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,38 @@ async fn async_main() {
};
call_once(c).await;
}

fn force_fnonce<T>(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T {
f
}

// Capture something with `move`, but infer to `AsyncFnOnce`
{
let x = Hello(6);
let c = force_fnonce(async move || {
println!("{x:?}");
});
call_once(c).await;

let x = &Hello(7);
let c = force_fnonce(async move || {
println!("{x:?}");
});
call_once(c).await;
}

// Capture something by-ref, but infer to `AsyncFnOnce`
{
let x = Hello(8);
let c = force_fnonce(async || {
println!("{x:?}");
});
call_once(c).await;

let x = &Hello(9);
let c = force_fnonce(async || {
println!("{x:?}");
});
call_once(c).await;
}
}
4 changes: 4 additions & 0 deletions tests/pass/async-closure-captures.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ Hello(3)
Hello(4)
Hello(4)
Hello(5)
Hello(6)
Hello(7)
Hello(8)
Hello(9)

0 comments on commit 5d6f914

Please sign in to comment.