Skip to content
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

Introduce support for async gen blocks #118420

Merged
merged 7 commits into from
Dec 8, 2023
Merged

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Nov 28, 2023

I'm delighted to demonstrate that async gen block are not very difficult to support. They're simply coroutines that yield Poll<Option<T>> and return ().

This PR is WIP and in draft mode for now -- I'm mostly putting it up to show folks that it's possible. This PR needs a lang-team experiment associated with it or possible an RFC, since I don't think it falls under the jurisdiction of the gen RFC that was recently authored by oli (rust-lang/rfcs#3513, #117078).

Technical note on the pre-generator-transform yield type:

The reason that the underlying coroutines yield Poll<Option<T>> and not Poll<T> (which would make more sense, IMO, for the pre-transformed coroutine), is because the TransformVisitor that is used to turn coroutines into built-in state machine functions would have to destructure and reconstruct the latter into the former, which requires at least inserting a new basic block (for a switchInt terminator, to match on the Poll discriminant).

This does mean that the desugaring (at the rustc_ast_lowering level) of async gen blocks is a bit more involved. However, since we already need to intercept both .await and yield operators, I don't consider it much of a technical burden.

r? @ghost

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative labels Nov 28, 2023
@compiler-errors
Copy link
Member Author

Importantly, this still needs a test 💀

I'll come up with one that uses a no-op waker or something.

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Contributor

bors commented Nov 30, 2023

☔ The latest upstream changes (presumably #118473) made this pull request unmergeable. Please resolve the merge conflicts.

@traviscross
Copy link
Contributor

@rustbot labels +I-lang-nominated

Nominating based on discussion to confirm that we're happy for async gen to land in nightly under the same experimental auspices as for gen.

@rustbot rustbot added the I-lang-nominated Nominated for discussion during a lang team meeting. label Dec 4, 2023
@rust-log-analyzer

This comment has been minimized.

@scottmcm
Copy link
Member

scottmcm commented Dec 6, 2023

We discussed this briefly in the lang triage meeting today, and agreed that this is free to continue for nightly. (It would, of course, need an RFC before it could be considered for stabilization.)


Personally, I'd say that combining existing things is almost always fine for experimentation in nightly, especially if it's something that T-compiler considers "not difficult".

@traviscross
Copy link
Contributor

@rustbot labels -I-lang-nominated

As scottmcm said, the consensus was that this is OK to land. Removing the nomination.

@rustbot rustbot removed the I-lang-nominated Nominated for discussion during a lang team meeting. label Dec 6, 2023
@compiler-errors compiler-errors marked this pull request as ready for review December 7, 2023 18:44
@compiler-errors
Copy link
Member Author

I believe this is ready for review

r? @eholk, though please reassign if you're busy

@rustbot
Copy link
Collaborator

rustbot commented Dec 7, 2023

Some changes occurred in src/tools/rustfmt

cc @rust-lang/rustfmt

Some changes occurred to the core trait solver

cc @rust-lang/initiative-trait-system-refactor

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rust-log-analyzer

This comment has been minimized.

Copy link
Contributor

@eholk eholk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just some minor questions/suggestions that you can take or not. Nothing blocking, so feel free to r=me after making any changes you want to take.

Comment on lines 128 to 130
pub fn async_gen_pending() -> Self {
Poll::Pending
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, could this be a constant like FINISHED? Or is there not much benefit to doing that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but in the process, I found a bug in hir typeck.

compiler/rustc_borrowck/src/diagnostics/region_name.rs Outdated Show resolved Hide resolved
compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs Outdated Show resolved Hide resolved
compiler/rustc_ast_lowering/src/expr.rs Show resolved Hide resolved
compiler/rustc_ast_lowering/src/lib.rs Show resolved Hide resolved
compiler/rustc_mir_transform/src/coroutine.rs Show resolved Hide resolved
compiler/rustc_parse/src/parser/expr.rs Outdated Show resolved Hide resolved
@rust-log-analyzer

This comment has been minimized.

@@ -771,6 +771,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let args = self.fresh_args_for_item(span, def_id);
let ty = item_ty.instantiate(self.tcx, args);

self.write_args(hir_id, args);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@compiler-errors
Copy link
Member Author

@bors r=eholk

@bors
Copy link
Contributor

bors commented Dec 8, 2023

📌 Commit a8c7761 has been approved by eholk

It is now in the queue for this repository.

@bors bors removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Dec 8, 2023
@compiler-errors
Copy link
Member Author

Rebased

@bors r=eholk

@bors
Copy link
Contributor

bors commented Dec 8, 2023

📌 Commit 11375c8 has been approved by eholk

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Dec 8, 2023
@bors
Copy link
Contributor

bors commented Dec 8, 2023

⌛ Testing commit 11375c8 with merge f967532...

@bors
Copy link
Contributor

bors commented Dec 8, 2023

☀️ Test successful - checks-actions
Approved by: eholk
Pushing f967532 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Dec 8, 2023
@bors bors merged commit f967532 into rust-lang:master Dec 8, 2023
12 checks passed
@rustbot rustbot added this to the 1.76.0 milestone Dec 8, 2023
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (f967532): comparison URL.

Overall result: ❌ regressions - ACTION NEEDED

Next Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression-triaged along with sufficient written justification. If you cannot justify the regressions please open an issue or create a new PR that fixes the regressions, add a comment linking to the newly created issue or PR, and then add the perf-regression-triaged label to this PR.

@rustbot label: +perf-regression
cc @rust-lang/wg-compiler-performance

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
0.2% [0.1%, 0.3%] 23
Regressions ❌
(secondary)
0.4% [0.2%, 0.7%] 3
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.2% [0.1%, 0.3%] 23

Max RSS (memory usage)

Results

This 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.

mean range count
Regressions ❌
(primary)
4.8% [4.8%, 4.8%] 1
Regressions ❌
(secondary)
2.2% [0.7%, 5.0%] 3
Improvements ✅
(primary)
-2.5% [-4.7%, -1.5%] 4
Improvements ✅
(secondary)
-4.7% [-4.7%, -4.7%] 1
All ❌✅ (primary) -1.0% [-4.7%, 4.8%] 5

Cycles

Results

This 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.

mean range count
Regressions ❌
(primary)
0.7% [0.7%, 0.7%] 1
Regressions ❌
(secondary)
0.8% [0.8%, 0.8%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.7% [0.7%, 0.7%] 1

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 674.909s -> 676.179s (0.19%)
Artifact size: 314.05 MiB -> 314.02 MiB (-0.01%)

@rustbot rustbot added the perf-regression Performance regression. label Dec 8, 2023
@compiler-errors
Copy link
Member Author

I have literally no idea why this would be a perf regression. Any ideas, @rust-lang/wg-compiler-performance?

@Kobzol
Copy link
Contributor

Kobzol commented Dec 8, 2023

<  15,266,505  ???:
  -19,862,558    <hashbrown::raw::RawTable<(rustc_span::def_id::LocalDefId, rustc_hir::hir_id::ItemLocalId)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_span::def_id::LocalDefId, rustc_hir::hir_id::ItemLocalId, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
   19,850,279    <hashbrown::raw::RawTable<(rustc_ast::node_id::NodeId, rustc_hir::hir_id::ItemLocalId)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_ast::node_id::NodeId, rustc_hir::hir_id::ItemLocalId, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
    5,150,361    rustc_query_system::dep_graph::graph::hash_result::<&rustc_middle::ty::typeck_results::TypeckResults>
    2,563,601    <rustc_middle::ty::Ty as rustc_serialize::serialize::Decodable<rustc_middle::query::on_disk_cache::CacheDecoder>>::decode
    1,671,252    <&rustc_middle::ty::list::List<rustc_middle::ty::generic_args::GenericArg> as rustc_serialize::serialize::Decodable<rustc_middle::query::on_disk_cache::CacheDecoder>>::decode
    1,670,754    rustc_query_impl::plumbing::encode_query_results::<rustc_query_impl::query_impl::typeck::QueryType>::{closure#0}
    1,651,399    rustc_middle::ty::codec::encode_with_shorthand::<rustc_middle::query::on_disk_cache::CacheEncoder, rustc_middle::ty::Ty, <rustc_middle::query::on_disk_cache::CacheEncoder as rustc_type_ir::codec::TyEncoder>::type_shorthands>
    1,402,677    <rustc_middle::ty::typeck_results::TypeckResults as rustc_serialize::serialize::Decodable<rustc_middle::query::on_disk_cache::CacheDecoder>>::decode
   -1,317,168    <hashbrown::raw::RawTable<(rustc_span::def_id::LocalDefId, rustc_span::def_id::CrateNum)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_span::def_id::LocalDefId, rustc_span::def_id::CrateNum, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
    1,317,165    <hashbrown::raw::RawTable<(rustc_ast::node_id::NodeId, rustc_ast::node_id::NodeId)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_ast::node_id::NodeId, rustc_ast::node_id::NodeId, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
   -1,247,819    <hashbrown::raw::RawTable<(rustc_span::def_id::LocalDefId, alloc::vec::Vec<rustc_span::def_id::DefId>)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_span::def_id::LocalDefId, alloc::vec::Vec<rustc_span::def_id::DefId>, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
    1,247,819    <hashbrown::raw::RawTable<(rustc_ast::node_id::NodeId, rustc_hir::def::PartialRes)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_ast::node_id::NodeId, rustc_hir::def::PartialRes, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
     -851,023    rustc_query_system::query::plumbing::ensure_must_run::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::VecCache<rustc_span::def_id::LocalDefId, rustc_middle::query::erase::Erased<[u8; 0]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt>
      820,268    rustc_query_system::query::plumbing::ensure_must_run::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::VecCache<rustc_hir::hir_id::OwnerId, rustc_middle::query::erase::Erased<[u8; 1]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt>
      460,984    <rustc_middle::ty::context::CtxtInterners>::intern_ty
     -413,385    <hashbrown::raw::RawTable<(rustc_span::def_id::LocalDefId, ())>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_span::def_id::LocalDefId, (), core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
      395,094    <hashbrown::raw::RawTable<(rustc_ast::node_id::NodeId, ())>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_ast::node_id::NodeId, (), core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
      352,761    <rustc_expand::mbe::macro_rules::MacroRulesMacroExpander as rustc_expand::base::TTMacroExpander>::expand
     -326,412    <hashbrown::raw::RawTable<(rustc_span::def_id::LocalDefId, rustc_resolve::late::LifetimeUseSet)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_span::def_id::LocalDefId, rustc_resolve::late::LifetimeUseSet, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
      326,327    <hashbrown::raw::RawTable<(rustc_ast::node_id::NodeId, rustc_resolve::Module)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_ast::node_id::NodeId, rustc_resolve::Module, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
      161,746    <rustc_ast_lowering::item::ItemLowerer>::lower_node
      130,217    <rustc_resolve::Resolver>::resolve_path_with_ribs
      123,216    <&rustc_middle::ty::list::List<rustc_type_ir::canonical::CanonicalVarInfo<rustc_middle::ty::context::TyCtxt>> as rustc_data_structures::stable_hasher::HashStable<rustc_query_system::ich::hcx::StableHashingContext>>::hash_stable
     -109,488    <rustc_parse::parser::Parser>::parse_expr_assoc_with
     -102,110    <rustc_ast_lowering::LoweringContext>::lower_qpath
      -88,115    rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::DefaultCache<rustc_span::def_id::DefId, rustc_middle::query::erase::Erased<[u8; 8]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, true>
       84,242    <rustc_parse::parser::Parser>::parse_path_inner
      -82,416    <hashbrown::raw::RawTable<(rustc_span::def_id::LocalDefId, alloc::vec::Vec<(rustc_middle::hir::place::Place, rustc_middle::mir::syntax::FakeReadCause, rustc_hir::hir_id::HirId)>)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_span::def_id::LocalDefId, alloc::vec::Vec<(rustc_middle::hir::place::Place, rustc_middle::mir::syntax::FakeReadCause, rustc_hir::hir_id::HirId)>, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
       82,416    <hashbrown::raw::RawTable<(rustc_hir::hir_id::ItemLocalId, alloc::vec::Vec<rustc_middle::ty::Ty>)>>::reserve_rehash::<hashbrown::map::make_hasher<rustc_hir::hir_id::ItemLocalId, alloc::vec::Vec<rustc_middle::ty::Ty>, core::hash::BuildHasherDefault<rustc_hash::FxHasher>>::{closure#0}>
       79,410    rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::VecCache<rustc_span::def_id::LocalDefId, rustc_middle::query::erase::Erased<[u8; 8]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, true>
      -63,035    rustc_middle::ty::codec::encode_with_shorthand::<rustc_metadata::rmeta::encoder::EncodeContext, rustc_middle::ty::Ty, <rustc_metadata::rmeta::encoder::EncodeContext as rustc_type_ir::codec::TyEncoder>::type_shorthands>
       60,309    <alloc::sync::Arc<[rustc_span::symbol::Symbol]>>::drop_slow
      -57,726    rustc_data_structures::unord::hash_iter_order_independent::<rustc_query_system::ich::hcx::StableHashingContext, &rustc_span::def_id::LocalDefId, std::collections::hash::set::Iter<rustc_span::def_id::LocalDefId>>
      -52,483    <rustc_infer::infer::InferCtxt>::probe::<core::result::Result<rustc_middle::traits::select::EvaluationResult, rustc_middle::traits::select::OverflowError>, <rustc_trait_selection::traits::select::SelectionContext>::evaluation_probe<<rustc_trait_selection::traits::select::SelectionContext>::evaluate_root_obligation::{closure#0}>::{closure#0}>
       52,188    <rustc_trait_selection::traits::select::SelectionContext>::evaluation_probe::<<rustc_trait_selection::traits::select::SelectionContext>::evaluate_root_obligation::{closure#0}>
       49,326    <rustc_ast_lowering::LoweringContext>::lower_ty_direct
      -47,664    <rustc_resolve::Resolver>::early_resolve_ident_in_lexical_scope
      -38,539    <rustc_parse::parser::Parser>::parse_expr_dot_or_call
       34,695    rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::DefaultCache<rustc_span::def_id::DefId, rustc_middle::query::erase::Erased<[u8; 16]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, true>
       33,320    rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::DefaultCache<rustc_span::def_id::DefId, rustc_middle::query::erase::Erased<[u8; 24]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, true>
      -31,609    <rustc_resolve::Resolver as rustc_expand::base::ResolverExpand>::resolve_macro_invocation
      -27,378    <rustc_resolve::late::LateResolutionVisitor>::with_lifetime_rib::<(), <rustc_resolve::late::LateResolutionVisitor>::resolve_const_body::{closure#0}>
       27,378    <rustc_resolve::late::LateResolutionVisitor>::resolve_const_body
       25,110    <rustc_lint::levels::TopDown as rustc_lint::levels::LintLevelsProvider>::get_lint_level
       22,697    <rustc_resolve::late::LateResolutionVisitor as rustc_ast::visit::Visitor>::visit_item
  • self profile queries show slightly more time spent in typeck. Seems like more type checking is happening?

In any case, I don't think that the regression is especially bad, cycles and wall-time are a wash, and this PR does introduce new functionality/feature after all.

@compiler-errors compiler-errors deleted the async-gen branch December 9, 2023 01:06
flip1995 pushed a commit to flip1995/rust that referenced this pull request Dec 16, 2023
Introduce support for `async gen` blocks

I'm delighted to demonstrate that `async gen` block are not very difficult to support. They're simply coroutines that yield `Poll<Option<T>>` and return `()`.

**This PR is WIP and in draft mode for now** -- I'm mostly putting it up to show folks that it's possible. This PR needs a lang-team experiment associated with it or possible an RFC, since I don't think it falls under the jurisdiction of the `gen` RFC that was recently authored by oli (rust-lang/rfcs#3513, rust-lang#117078).

### Technical note on the pre-generator-transform yield type:

The reason that the underlying coroutines yield `Poll<Option<T>>` and not `Poll<T>` (which would make more sense, IMO, for the pre-transformed coroutine), is because the `TransformVisitor` that is used to turn coroutines into built-in state machine functions would have to destructure and reconstruct the latter into the former, which requires at least inserting a new basic block (for a `switchInt` terminator, to match on the `Poll` discriminant).

This does mean that the desugaring (at the `rustc_ast_lowering` level) of `async gen` blocks is a bit more involved. However, since we already need to intercept both `.await` and `yield` operators, I don't consider it much of a technical burden.

r? `@ghost`
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Apr 6, 2024
…ler-errors

Hide async_gen_internals from standard library documentation

These are from rust-lang#118420. It doesn't appear that there is any intention to ever make these APIs available to user code. These are just conveniences meant for the compiler's implementation of `async gen`. I don't think having them featured in documentation in <https://doc.rust-lang.org/1.77.1/core/task/enum.Poll.html> is appropriate.

![image](https://github.com/rust-lang/rust/assets/1940490/0a8ae90d-5c83-4ab1-b08a-50bad2433d69)
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 6, 2024
Rollup merge of rust-lang#123528 - dtolnay:asyncgeninternals, r=compiler-errors

Hide async_gen_internals from standard library documentation

These are from rust-lang#118420. It doesn't appear that there is any intention to ever make these APIs available to user code. These are just conveniences meant for the compiler's implementation of `async gen`. I don't think having them featured in documentation in <https://doc.rust-lang.org/1.77.1/core/task/enum.Poll.html> is appropriate.

![image](https://github.com/rust-lang/rust/assets/1940490/0a8ae90d-5c83-4ab1-b08a-50bad2433d69)
calebcartwright pushed a commit to calebcartwright/rust that referenced this pull request Jun 22, 2024
Introduce support for `async gen` blocks

I'm delighted to demonstrate that `async gen` block are not very difficult to support. They're simply coroutines that yield `Poll<Option<T>>` and return `()`.

**This PR is WIP and in draft mode for now** -- I'm mostly putting it up to show folks that it's possible. This PR needs a lang-team experiment associated with it or possible an RFC, since I don't think it falls under the jurisdiction of the `gen` RFC that was recently authored by oli (rust-lang/rfcs#3513, rust-lang#117078).

### Technical note on the pre-generator-transform yield type:

The reason that the underlying coroutines yield `Poll<Option<T>>` and not `Poll<T>` (which would make more sense, IMO, for the pre-transformed coroutine), is because the `TransformVisitor` that is used to turn coroutines into built-in state machine functions would have to destructure and reconstruct the latter into the former, which requires at least inserting a new basic block (for a `switchInt` terminator, to match on the `Poll` discriminant).

This does mean that the desugaring (at the `rustc_ast_lowering` level) of `async gen` blocks is a bit more involved. However, since we already need to intercept both `.await` and `yield` operators, I don't consider it much of a technical burden.

r? `@ghost`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. perf-regression Performance regression. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants