Support async gen fn

This commit is contained in:
Michael Goulet 2023-12-05 21:45:01 +00:00
parent 2806c2df7b
commit a208bae00e
16 changed files with 115 additions and 104 deletions

View file

@ -23,8 +23,6 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late
parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015
.label = to use `async fn`, switch to Rust 2018 or later
parse_async_gen_fn = `async gen` functions are not supported
parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect

View file

@ -562,13 +562,6 @@ pub(crate) struct GenFn {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_async_gen_fn)]
pub(crate) struct AsyncGenFn {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_comma_after_base_struct)]
#[note]

View file

@ -1442,21 +1442,21 @@ impl<'a> Parser<'a> {
} 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) {
if this.is_gen_block(kw::Async, 0) || this.is_gen_block(kw::Gen, 1) {
// 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.eat_keyword(kw::Await) {
} 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 if this.token.uninterpolated_span().at_least_rust_2024() {
if this.is_gen_block(kw::Gen, 0) {
this.parse_gen_block()
} else {
this.parse_expr_lit()
}
} else {
this.parse_expr_lit()
}
@ -2235,8 +2235,8 @@ impl<'a> Parser<'a> {
let movability =
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() {
self.parse_asyncness(Case::Sensitive)
let coroutine_kind = if self.token.uninterpolated_span().at_least_rust_2018() {
self.parse_coroutine_kind(Case::Sensitive)
} else {
None
};
@ -2262,9 +2262,17 @@ impl<'a> Parser<'a> {
}
};
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
// Feature-gate `async ||` closures.
self.sess.gated_spans.gate(sym::async_closure, span);
match coroutine_kind {
Some(CoroutineKind::Async { span, .. }) => {
// Feature-gate `async ||` closures.
self.sess.gated_spans.gate(sym::async_closure, span);
}
Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => {
// Feature-gate `gen ||` and `async gen ||` closures.
// FIXME(gen_blocks): This perhaps should be a different gate.
self.sess.gated_spans.gate(sym::gen_blocks, span);
}
None => {}
}
if self.token.kind == TokenKind::Semi
@ -2285,7 +2293,7 @@ impl<'a> Parser<'a> {
binder,
capture_clause,
constness,
coroutine_kind: asyncness,
coroutine_kind,
movability,
fn_decl,
body,

View file

@ -2394,10 +2394,7 @@ impl<'a> Parser<'a> {
let constness = self.parse_constness(case);
let async_start_sp = self.token.span;
let asyncness = self.parse_asyncness(case);
let _gen_start_sp = self.token.span;
let genness = self.parse_genness(case);
let coroutine_kind = self.parse_coroutine_kind(case);
let unsafe_start_sp = self.token.span;
let unsafety = self.parse_unsafety(case);
@ -2405,7 +2402,7 @@ impl<'a> Parser<'a> {
let ext_start_sp = self.token.span;
let ext = self.parse_extern(case);
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
if let Some(CoroutineKind::Async { span, .. }) = coroutine_kind {
if span.is_rust_2015() {
self.sess.emit_err(errors::AsyncFnIn2015 {
span,
@ -2414,16 +2411,11 @@ impl<'a> Parser<'a> {
}
}
if let Some(CoroutineKind::Gen { span, .. }) = genness {
self.sess.gated_spans.gate(sym::gen_blocks, span);
}
if let (
Some(CoroutineKind::Async { span: async_span, .. }),
Some(CoroutineKind::Gen { span: gen_span, .. }),
) = (asyncness, genness)
{
self.sess.emit_err(errors::AsyncGenFn { span: async_span.to(gen_span) });
match coroutine_kind {
Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => {
self.sess.gated_spans.gate(sym::gen_blocks, span);
}
Some(CoroutineKind::Async { .. }) | None => {}
}
if !self.eat_keyword_case(kw::Fn, case) {
@ -2442,7 +2434,7 @@ impl<'a> Parser<'a> {
// We may be able to recover
let mut recover_constness = constness;
let mut recover_asyncness = asyncness;
let mut recover_coroutine_kind = coroutine_kind;
let mut recover_unsafety = unsafety;
// This will allow the machine fix to directly place the keyword in the correct place or to indicate
// that the keyword is already present and the second instance should be removed.
@ -2455,15 +2447,24 @@ impl<'a> Parser<'a> {
}
}
} else if self.check_keyword(kw::Async) {
match asyncness {
match coroutine_kind {
Some(CoroutineKind::Async { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::AsyncGen { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::Gen { .. }) => {
panic!("not sure how to recover here")
recover_coroutine_kind = Some(CoroutineKind::AsyncGen {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
});
// FIXME(gen_blocks): This span is wrong, didn't want to think about it.
Some(WrongKw::Misplaced(unsafe_start_sp))
}
None => {
recover_asyncness = Some(CoroutineKind::Async {
recover_coroutine_kind = Some(CoroutineKind::Async {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
@ -2561,7 +2562,7 @@ impl<'a> Parser<'a> {
return Ok(FnHeader {
constness: recover_constness,
unsafety: recover_unsafety,
coroutine_kind: recover_asyncness,
coroutine_kind: recover_coroutine_kind,
ext,
});
}
@ -2571,12 +2572,6 @@ impl<'a> Parser<'a> {
}
}
let coroutine_kind = match asyncness {
Some(CoroutineKind::Async { .. }) => asyncness,
Some(CoroutineKind::Gen { .. }) => unreachable!("asycness cannot be Gen"),
None => genness,
};
Ok(FnHeader { constness, unsafety, coroutine_kind, ext })
}

View file

@ -1125,23 +1125,30 @@ impl<'a> Parser<'a> {
}
/// Parses asyncness: `async` or nothing.
fn parse_asyncness(&mut self, case: Case) -> Option<CoroutineKind> {
fn parse_coroutine_kind(&mut self, case: Case) -> Option<CoroutineKind> {
let span = self.token.uninterpolated_span();
if self.eat_keyword_case(kw::Async, case) {
let span = self.prev_token.uninterpolated_span();
Some(CoroutineKind::Async {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
} else {
None
}
}
/// Parses genness: `gen` or nothing.
fn parse_genness(&mut self, case: Case) -> Option<CoroutineKind> {
if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) {
let span = self.prev_token.uninterpolated_span();
// FIXME(gen_blocks): Do we want to unconditionally parse `gen` and then
// error if edition <= 2024, like we do with async and edition <= 2018?
if self.token.uninterpolated_span().at_least_rust_2024()
&& self.eat_keyword_case(kw::Gen, case)
{
let gen_span = self.prev_token.uninterpolated_span();
Some(CoroutineKind::AsyncGen {
span: span.to(gen_span),
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
} else {
Some(CoroutineKind::Async {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
}
} else if self.token.uninterpolated_span().at_least_rust_2024()
&& self.eat_keyword_case(kw::Gen, case)
{
Some(CoroutineKind::Gen {
span,
closure_id: DUMMY_NODE_ID,