Support async recursive calls (as long as they have indirection)#117703
Support async recursive calls (as long as they have indirection)#117703bors merged 7 commits intorust-lang:masterfrom
Conversation
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…try>
Support async recursive calls (as long as they have indirection)
TL;DR: This code should work
```
async fn foo() {
Box::pin(foo()).await;
}
```
r? `@ghost` while I write up a description, etc.
This comment has been minimized.
This comment has been minimized.
|
💔 Test failed - checks-actions |
d0af0e3 to
ac3c93c
Compare
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…try>
Support async recursive calls (as long as they have indirection)
TL;DR: This code should work
```
async fn foo() {
Box::pin(foo()).await;
}
```
r? `@ghost` while I write up a description, etc.
This comment has been minimized.
This comment has been minimized.
ac3c93c to
8b4b867
Compare
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
…try> Support async recursive calls (as long as they have indirection) Before rust-lang#101692, we stored coroutine witness types directly inside of the coroutine. That means that a coroutine could not contain itself (as a witness field) without creating a cycle in the type representation of the coroutine, which we detected with the `OpaqueTypeExpander`, which is used to detect cycles when expanding opaque types after that are inferred to contain themselves. After `-Zdrop-tracking-mir` was stabilized, we no longer store these generator witness fields directly, but instead behind a def-id based query. That means there is no technical obstacle in the compiler preventing coroutines from containing themselves per se, other than the fact that for a coroutine to have a non-infinite layout, it must contain itself wrapped in a layer of allocation indirection (like a `Box`). This means that it should be valid for this code to work: ``` async fn async_fibonacci(i: u32) -> u32 { if i == 0 || i == 1 { i } else { Box::pin(async_fibonacci(i - 1)).await + Box::pin(async_fibonacci(i - 2)).await } } ``` Whereas previously, you'd need to coerce the future to `Pin<Box<dyn Future<Output = ...>>` before `await`ing it, to prevent the async's desugared coroutine from containing itself across as await point. This PR does two things: 1. Remove the behavior from `OpaqueTypeExpander` where it intentionally fetches and walks through the coroutine's witness fields. This was kept around after `-Zdrop-tracking-mir` was stabilized so we would not be introducing new stable behavior, and to preserve the much better diagnostics of async recursion compared to a layout error. 2. Reworks the way we report layout errors having to do with coroutines, to make up for the diagnostic regressions introduced by (1.). We actually do even better now, pointing out the call sites of the recursion!
This comment has been minimized.
This comment has been minimized.
|
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
|
@bors r=lcnr |
|
☔ The latest upstream changes (presumably #119606) made this pull request unmergeable. Please resolve the merge conflicts. |
Also don't recomment recursive_async crate anymore Co-authored-by: lcnr <rust@lcnr.de>
5b5393d to
9a75603
Compare
|
rebased @bors r=lcnr |
|
☀️ Test successful - checks-actions |
|
Finished benchmarking commit (dc64103): comparison URL. Overall result: ❌✅ regressions and improvements - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesThis benchmark run did not return any relevant results for this metric. Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 669.826s -> 669.888s (0.01%) |
@rustbot label: +perf-regression-triaged |
Before #101692, we stored coroutine witness types directly inside of the coroutine. That means that a coroutine could not contain itself (as a witness field) without creating a cycle in the type representation of the coroutine, which we detected with the
OpaqueTypeExpander, which is used to detect cycles when expanding opaque types after that are inferred to contain themselves.After
-Zdrop-tracking-mirwas stabilized, we no longer store these generator witness fields directly, but instead behind a def-id based query. That means there is no technical obstacle in the compiler preventing coroutines from containing themselves per se, other than the fact that for a coroutine to have a non-infinite layout, it must contain itself wrapped in a layer of allocation indirection (like aBox).This means that it should be valid for this code to work:
Whereas previously, you'd need to coerce the future to
Pin<Box<dyn Future<Output = ...>>beforeawaiting it, to prevent the async's desugared coroutine from containing itself across as await point.This PR does two things: