diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2024-04-05 16:38:51 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-05 16:38:51 +0200 |
| commit | 02ee8a8ceeff811adfb065028a48c1947d97ef91 (patch) | |
| tree | e03896d1ad6a90866cf7432102cfae1b4a53e3fd /src | |
| parent | f2f8d8b722fdacc6a6e02c2289e3e36571749a68 (diff) | |
| parent | 55e46612c1ccceb30a7a6acf11fd485f34e393e5 (diff) | |
| download | rust-02ee8a8ceeff811adfb065028a48c1947d97ef91.tar.gz rust-02ee8a8ceeff811adfb065028a48c1947d97ef91.zip | |
Rollup merge of #123350 - compiler-errors:async-closure-by-move, r=oli-obk
Actually use the inferred `ClosureKind` from signature inference in coroutine-closures
A follow-up to https://github.com/rust-lang/rust/pull/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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/tools/miri/tests/pass/async-closure-captures.rs | 34 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass/async-closure-captures.stdout | 4 |
2 files changed, 38 insertions, 0 deletions
diff --git a/src/tools/miri/tests/pass/async-closure-captures.rs b/src/tools/miri/tests/pass/async-closure-captures.rs index 3e33de32efb..cac26bfe146 100644 --- a/src/tools/miri/tests/pass/async-closure-captures.rs +++ b/src/tools/miri/tests/pass/async-closure-captures.rs @@ -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; + } } diff --git a/src/tools/miri/tests/pass/async-closure-captures.stdout b/src/tools/miri/tests/pass/async-closure-captures.stdout index a0db6d236fe..42a7999b2dc 100644 --- a/src/tools/miri/tests/pass/async-closure-captures.stdout +++ b/src/tools/miri/tests/pass/async-closure-captures.stdout @@ -8,3 +8,7 @@ Hello(3) Hello(4) Hello(4) Hello(5) +Hello(6) +Hello(7) +Hello(8) +Hello(9) |
