Auto merge of #118420 - compiler-errors:async-gen, r=eholk
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 (https://github.com/rust-lang/rfcs/pull/3513, https://github.com/rust-lang/rust/issues/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`
This commit is contained in:
commit
f967532a47
61 changed files with 1120 additions and 357 deletions
|
@ -156,29 +156,33 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
|||
|
||||
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
|
||||
if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind {
|
||||
if let Some(
|
||||
CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. },
|
||||
) = sig.header.coro_kind
|
||||
{
|
||||
self.visit_generics(generics);
|
||||
match sig.header.coroutine_kind {
|
||||
Some(
|
||||
CoroutineKind::Async { closure_id, .. }
|
||||
| CoroutineKind::Gen { closure_id, .. }
|
||||
| CoroutineKind::AsyncGen { closure_id, .. },
|
||||
) => {
|
||||
self.visit_generics(generics);
|
||||
|
||||
// For async functions, we need to create their inner defs inside of a
|
||||
// closure to match their desugared representation. Besides that,
|
||||
// we must mirror everything that `visit::walk_fn` below does.
|
||||
self.visit_fn_header(&sig.header);
|
||||
for param in &sig.decl.inputs {
|
||||
self.visit_param(param);
|
||||
// For async functions, we need to create their inner defs inside of a
|
||||
// closure to match their desugared representation. Besides that,
|
||||
// we must mirror everything that `visit::walk_fn` below does.
|
||||
self.visit_fn_header(&sig.header);
|
||||
for param in &sig.decl.inputs {
|
||||
self.visit_param(param);
|
||||
}
|
||||
self.visit_fn_ret_ty(&sig.decl.output);
|
||||
// If this async fn has no body (i.e. it's an async fn signature in a trait)
|
||||
// then the closure_def will never be used, and we should avoid generating a
|
||||
// def-id for it.
|
||||
if let Some(body) = body {
|
||||
let closure_def =
|
||||
self.create_def(closure_id, kw::Empty, DefKind::Closure, span);
|
||||
self.with_parent(closure_def, |this| this.visit_block(body));
|
||||
}
|
||||
return;
|
||||
}
|
||||
self.visit_fn_ret_ty(&sig.decl.output);
|
||||
// If this async fn has no body (i.e. it's an async fn signature in a trait)
|
||||
// then the closure_def will never be used, and we should avoid generating a
|
||||
// def-id for it.
|
||||
if let Some(body) = body {
|
||||
let closure_def =
|
||||
self.create_def(closure_id, kw::Empty, DefKind::Closure, span);
|
||||
self.with_parent(closure_def, |this| this.visit_block(body));
|
||||
}
|
||||
return;
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,10 +288,11 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
|||
// Async closures desugar to closures inside of closures, so
|
||||
// we must create two defs.
|
||||
let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
|
||||
match closure.coro_kind {
|
||||
match closure.coroutine_kind {
|
||||
Some(
|
||||
CoroutineKind::Async { closure_id, .. }
|
||||
| CoroutineKind::Gen { closure_id, .. },
|
||||
| CoroutineKind::Gen { closure_id, .. }
|
||||
| CoroutineKind::AsyncGen { closure_id, .. },
|
||||
) => self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span),
|
||||
None => closure_def,
|
||||
}
|
||||
|
|
|
@ -916,8 +916,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
|||
&sig.decl.output,
|
||||
);
|
||||
|
||||
if let Some((coro_node_id, _)) =
|
||||
sig.header.coro_kind.map(|coro_kind| coro_kind.return_id())
|
||||
if let Some((coro_node_id, _)) = sig
|
||||
.header
|
||||
.coroutine_kind
|
||||
.map(|coroutine_kind| coroutine_kind.return_id())
|
||||
{
|
||||
this.record_lifetime_params_for_impl_trait(coro_node_id);
|
||||
}
|
||||
|
@ -942,8 +944,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
|||
this.visit_generics(generics);
|
||||
|
||||
let declaration = &sig.decl;
|
||||
let coro_node_id =
|
||||
sig.header.coro_kind.map(|coro_kind| coro_kind.return_id());
|
||||
let coro_node_id = sig
|
||||
.header
|
||||
.coroutine_kind
|
||||
.map(|coroutine_kind| coroutine_kind.return_id());
|
||||
|
||||
this.with_lifetime_rib(
|
||||
LifetimeRibKind::AnonymousCreateParameter {
|
||||
|
@ -4294,7 +4298,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
//
|
||||
// Similarly, `gen |x| ...` gets desugared to `|x| gen {...}`, so we handle that too.
|
||||
ExprKind::Closure(box ast::Closure {
|
||||
coro_kind: Some(_),
|
||||
coroutine_kind: Some(_),
|
||||
ref fn_decl,
|
||||
ref body,
|
||||
..
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue