From 1d78ce681e4bf0c0259fc97e422298d68635dc4f Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 12 Dec 2023 20:08:34 +0000
Subject: [PATCH] Actually parse async gen blocks correctly

---
 compiler/rustc_parse/src/parser/expr.rs       | 27 ++++++++++------
 tests/ui/coroutine/async-gen-deduce-yield.rs  | 14 ++++++++
 .../feature-gate-gen_blocks.e2024.stderr      | 32 ++++++++++++++++---
 .../feature-gate-gen_blocks.none.stderr       | 14 +++++++-
 .../feature-gates/feature-gate-gen_blocks.rs  | 15 ++++++++-
 5 files changed, 87 insertions(+), 15 deletions(-)
 create mode 100644 tests/ui/coroutine/async-gen-deduce-yield.rs

diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 3c0627526be..3606c0fa098 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1440,21 +1440,23 @@ impl<'a> Parser<'a> {
             } else if this.eat_keyword(kw::Underscore) {
                 Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))
             } else if this.token.uninterpolated_span().at_least_rust_2018() {
-                // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
-                if this.check_keyword(kw::Async) {
+                // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
+                if this.token.uninterpolated_span().at_least_rust_2024()
+                    // check for `gen {}` and `gen move {}`
+                    // or `async gen {}` and `async gen move {}`
+                    && (this.is_gen_block(kw::Gen, 0)
+                        || (this.check_keyword(kw::Async) && this.is_gen_block(kw::Gen, 1)))
+                {
+                    // FIXME: (async) gen closures aren't yet parsed.
+                    this.parse_gen_block()
+                } else if this.check_keyword(kw::Async) {
                     // FIXME(gen_blocks): Parse `gen async` and suggest swap
                     if this.is_gen_block(kw::Async, 0) {
                         // Check for `async {` and `async move {`,
-                        // or `async gen {` and `async gen move {`.
                         this.parse_gen_block()
                     } else {
                         this.parse_expr_closure()
                     }
-                } else if this.token.uninterpolated_span().at_least_rust_2024()
-                    && (this.is_gen_block(kw::Gen, 0)
-                        || (this.check_keyword(kw::Async) && this.is_gen_block(kw::Gen, 1)))
-                {
-                    this.parse_gen_block()
                 } else if this.eat_keyword_noexpect(kw::Await) {
                     this.recover_incorrect_await_syntax(lo, this.prev_token.span)
                 } else {
@@ -3219,9 +3221,16 @@ impl<'a> Parser<'a> {
             if self.eat_keyword(kw::Gen) { GenBlockKind::AsyncGen } else { GenBlockKind::Async }
         } else {
             assert!(self.eat_keyword(kw::Gen));
-            self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span));
             GenBlockKind::Gen
         };
+        match kind {
+            GenBlockKind::Async => {
+                // `async` blocks are stable
+            }
+            GenBlockKind::Gen | GenBlockKind::AsyncGen => {
+                self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.prev_token.span));
+            }
+        }
         let capture_clause = self.parse_capture_clause()?;
         let (attrs, body) = self.parse_inner_attrs_and_block()?;
         let kind = ExprKind::Gen(capture_clause, body, kind);
diff --git a/tests/ui/coroutine/async-gen-deduce-yield.rs b/tests/ui/coroutine/async-gen-deduce-yield.rs
new file mode 100644
index 00000000000..9ccc8ee41f6
--- /dev/null
+++ b/tests/ui/coroutine/async-gen-deduce-yield.rs
@@ -0,0 +1,14 @@
+// compile-flags: --edition 2024 -Zunstable-options
+// check-pass
+
+#![feature(async_iterator, gen_blocks)]
+
+use std::async_iter::AsyncIterator;
+
+fn deduce() -> impl AsyncIterator<Item = ()> {
+    async gen {
+        yield Default::default();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr
index 1462c41e957..c582ca7ba3d 100644
--- a/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr
+++ b/tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr
@@ -2,16 +2,34 @@ error[E0658]: gen blocks are experimental
   --> $DIR/feature-gate-gen_blocks.rs:5:5
    |
 LL |     gen {};
-   |     ^^^^^
+   |     ^^^
    |
    = note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information
    = help: add `#![feature(gen_blocks)]` to the crate attributes to enable
 
 error[E0658]: gen blocks are experimental
-  --> $DIR/feature-gate-gen_blocks.rs:13:5
+  --> $DIR/feature-gate-gen_blocks.rs:12:5
+   |
+LL |     async gen {};
+   |     ^^^^^^^^^
+   |
+   = note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information
+   = help: add `#![feature(gen_blocks)]` to the crate attributes to enable
+
+error[E0658]: gen blocks are experimental
+  --> $DIR/feature-gate-gen_blocks.rs:22:5
    |
 LL |     gen {};
-   |     ^^^^^
+   |     ^^^
+   |
+   = note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information
+   = help: add `#![feature(gen_blocks)]` to the crate attributes to enable
+
+error[E0658]: gen blocks are experimental
+  --> $DIR/feature-gate-gen_blocks.rs:25:5
+   |
+LL |     async gen {};
+   |     ^^^^^^^^^
    |
    = note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information
    = help: add `#![feature(gen_blocks)]` to the crate attributes to enable
@@ -22,7 +40,13 @@ error[E0282]: type annotations needed
 LL |     gen {};
    |         ^^ cannot infer type
 
-error: aborting due to 3 previous errors
+error[E0282]: type annotations needed
+  --> $DIR/feature-gate-gen_blocks.rs:12:15
+   |
+LL |     async gen {};
+   |               ^^ cannot infer type
+
+error: aborting due to 6 previous errors
 
 Some errors have detailed explanations: E0282, E0658.
 For more information about an error, try `rustc --explain E0282`.
diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr b/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr
index 56f8309a69f..b4b37f0e638 100644
--- a/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr
+++ b/tests/ui/feature-gates/feature-gate-gen_blocks.none.stderr
@@ -1,9 +1,21 @@
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen`
+  --> $DIR/feature-gate-gen_blocks.rs:12:11
+   |
+LL |     async gen {};
+   |           ^^^ expected one of 8 possible tokens
+
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen`
+  --> $DIR/feature-gate-gen_blocks.rs:25:11
+   |
+LL |     async gen {};
+   |           ^^^ expected one of 8 possible tokens
+
 error[E0422]: cannot find struct, variant or union type `gen` in this scope
   --> $DIR/feature-gate-gen_blocks.rs:5:5
    |
 LL |     gen {};
    |     ^^^ not found in this scope
 
-error: aborting due to 1 previous error
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0422`.
diff --git a/tests/ui/feature-gates/feature-gate-gen_blocks.rs b/tests/ui/feature-gates/feature-gate-gen_blocks.rs
index e2e1574a36a..ff9a0b139c0 100644
--- a/tests/ui/feature-gates/feature-gate-gen_blocks.rs
+++ b/tests/ui/feature-gates/feature-gate-gen_blocks.rs
@@ -1,15 +1,28 @@
 // revisions: e2024 none
 //[e2024] compile-flags: --edition 2024 -Zunstable-options
 
-fn main() {
+fn test_gen() {
     gen {};
     //[none]~^ ERROR: cannot find struct, variant or union type `gen`
     //[e2024]~^^ ERROR: gen blocks are experimental
     //[e2024]~| ERROR: type annotations needed
 }
 
+fn test_async_gen() {
+    async gen {};
+    //[none]~^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen`
+    //[e2024]~^^ ERROR: gen blocks are experimental
+    //[e2024]~| ERROR: type annotations needed
+}
+
+fn main() {}
+
 #[cfg(FALSE)]
 fn foo() {
     gen {};
     //[e2024]~^ ERROR: gen blocks are experimental
+
+    async gen {};
+    //[e2024]~^ ERROR: gen blocks are experimental
+    //[none]~^^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `gen`
 }