-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support async recursive calls (as long as they have indirection) #117703
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.
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 |
Pkgsrc changes: * Adapt checksums and patches. Upstream chnages: Version 1.77.0 (2024-03-21) ========================== - [Reveal opaque types within the defining body for exhaustiveness checking.] (rust-lang/rust#116821) - [Stabilize C-string literals.] (rust-lang/rust#117472) - [Stabilize THIR unsafeck.] (rust-lang/rust#117673) - [Add lint `static_mut_refs` to warn on references to mutable statics.] (rust-lang/rust#117556) - [Support async recursive calls (as long as they have indirection).] (rust-lang/rust#117703) - [Undeprecate lint `unstable_features` and make use of it in the compiler.] (rust-lang/rust#118639) - [Make inductive cycles in coherence ambiguous always.] (rust-lang/rust#118649) - [Get rid of type-driven traversal in const-eval interning] (rust-lang/rust#119044), only as a [future compatiblity lint] (rust-lang/rust#122204) for now. - [Deny braced macro invocations in let-else.] (rust-lang/rust#119062) Compiler -------- - [Include lint `soft_unstable` in future breakage reports.] (rust-lang/rust#116274) - [Make `i128` and `u128` 16-byte aligned on x86-based targets.] (rust-lang/rust#116672) - [Use `--verbose` in diagnostic output.] (rust-lang/rust#119129) - [Improve spacing between printed tokens.] (rust-lang/rust#120227) - [Merge the `unused_tuple_struct_fields` lint into `dead_code`.] (rust-lang/rust#118297) - [Error on incorrect implied bounds in well-formedness check] (rust-lang/rust#118553), with a temporary exception for Bevy. - [Fix coverage instrumentation/reports for non-ASCII source code.] (rust-lang/rust#119033) - [Fix `fn`/`const` items implied bounds and well-formedness check.] (rust-lang/rust#120019) - [Promote `riscv32{im|imafc}-unknown-none-elf` targets to tier 2.] (rust-lang/rust#118704) - Add several new tier 3 targets: - [`aarch64-unknown-illumos`] (rust-lang/rust#112936) - [`hexagon-unknown-none-elf`] (rust-lang/rust#117601) - [`riscv32imafc-esp-espidf`] (rust-lang/rust#119738) - [`riscv32im-risc0-zkvm-elf`] (rust-lang/rust#117958) Refer to Rust's [platform support page][platform-support-doc] for more information on Rust's tiered platform support. Libraries --------- - [Implement `From<&[T; N]>` for `Cow<[T]>`.] (rust-lang/rust#113489) - [Remove special-case handling of `vec.split_off (0)`.](rust-lang/rust#119917) Stabilized APIs --------------- - [`array::each_ref`] (https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_ref) - [`array::each_mut`] (https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut) - [`core::net`] (https://doc.rust-lang.org/stable/core/net/index.html) - [`f32::round_ties_even`] (https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round_ties_even) - [`f64::round_ties_even`] (https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round_ties_even) - [`mem::offset_of!`] (https://doc.rust-lang.org/stable/std/mem/macro.offset_of.html) - [`slice::first_chunk`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.first_chunk) - [`slice::first_chunk_mut`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.first_chunk_mut) - [`slice::split_first_chunk`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_first_chunk) - [`slice::split_first_chunk_mut`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_first_chunk_mut) - [`slice::last_chunk`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.last_chunk) - [`slice::last_chunk_mut`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.last_chunk_mut) - [`slice::split_last_chunk`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_last_chunk) - [`slice::split_last_chunk_mut`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.split_last_chunk_mut) - [`slice::chunk_by`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunk_by) - [`slice::chunk_by_mut`] (https://doc.rust-lang.org/stable/std/primitive.slice.html#method.chunk_by_mut) - [`Bound::map`] (https://doc.rust-lang.org/stable/std/ops/enum.Bound.html#method.map) - [`File::create_new`] (https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.create_new) - [`Mutex::clear_poison`] (https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html#method.clear_poison) - [`RwLock::clear_poison`] (https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html#method.clear_poison) Cargo ----- - [Extend the build directive syntax with `cargo::`.] (rust-lang/cargo#12201) - [Stabilize metadata `id` format as `PackageIDSpec`.] (rust-lang/cargo#12914) - [Pull out as `cargo-util-schemas` as a crate.] (rust-lang/cargo#13178) - [Strip all debuginfo when debuginfo is not requested.] (rust-lang/cargo#13257) - [Inherit jobserver from env for all kinds of runners.] (rust-lang/cargo#12776) - [Deprecate rustc plugin support in cargo.] (rust-lang/cargo#13248) Rustdoc ----- - [Allows links in markdown headings.] (rust-lang/rust#117662) - [Search for tuples and unit by type with `()`.] (rust-lang/rust#118194) - [Clean up the source sidebar's hide button.] (rust-lang/rust#119066) - [Prevent JS injection from `localStorage`.] (rust-lang/rust#120250) Misc ---- - [Recommend version-sorting for all sorting in style guide.] (rust-lang/rust#115046) Internal Changes ---------------- These changes do not affect any public interfaces of Rust, but they represent significant improvements to the performance or internals of rustc and related tools. - [Add more weirdness to `weird-exprs.rs`.] (rust-lang/rust#119028)
Since Rust 1.77 (https://github.com/rust-lang/rust/blob/ba956ef4b00c91579cff9b2220358ee3a46d982f/RELEASES.md#version-1770-2024-03-21), async recursion is suppoerted out of the box. The example in rust-lang/rust#117703 shows how to use `Box::pin()` to avoid this error message: ``` note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future ``` This commit reduces the binary size by 1656 bytes.
Since Rust 1.77 (https://github.com/rust-lang/rust/blob/ba956ef4b00c91579cff9b2220358ee3a46d982f/RELEASES.md#version-1770-2024-03-21), async recursion is suppoerted out of the box. The example in rust-lang/rust#117703 shows how to use `Box::pin()` to avoid this error message: ``` note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future ``` This commit reduces the binary size by 1656 bytes.
Since Rust 1.77 (https://github.com/rust-lang/rust/blob/ba956ef4b00c91579cff9b2220358ee3a46d982f/RELEASES.md#version-1770-2024-03-21), async recursion is suppoerted out of the box. The example in rust-lang/rust#117703 shows how to use `Box::pin()` to avoid this error message: ``` note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future ``` This commit reduces the binary size by 1656 bytes.
Since Rust 1.77 (https://github.com/rust-lang/rust/blob/ba956ef4b00c91579cff9b2220358ee3a46d982f/RELEASES.md#version-1770-2024-03-21), async recursion is suppoerted out of the box. The example in rust-lang/rust#117703 shows how to use `Box::pin()` to avoid this error message: ``` note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future ``` This commit reduces the binary size by 1656 bytes.
Since Rust 1.77 (https://github.com/rust-lang/rust/blob/ba956ef4b00c91579cff9b2220358ee3a46d982f/RELEASES.md#version-1770-2024-03-21), async recursion is suppoerted out of the box. The example in rust-lang/rust#117703 shows how to use `Box::pin()` to avoid this error message: ``` note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future ``` This commit reduces the binary size by 1656 bytes.
Since Rust 1.77 (https://github.com/rust-lang/rust/blob/ba956ef4b00c91579cff9b2220358ee3a46d982f/RELEASES.md#version-1770-2024-03-21), async recursion is suppoerted out of the box. The example in rust-lang/rust#117703 shows how to use `Box::pin()` to avoid this error message: ``` note: a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future ``` This commit reduces the binary size by 1656 bytes.
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-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 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 = ...>>
beforeawait
ing it, to prevent the async's desugared coroutine from containing itself across as await point.This PR does two things: