Auto merge of #118457 - eholk:genfn, r=compiler-errors

Add support for `gen fn`

This builds on #116447 to add support for `gen fn` functions. For the most part we follow the same approach as desugaring `async fn`, but replacing `Future` with `Iterator` and `async {}` with `gen {}` for the body.

The version implemented here uses the return type of a `gen fn` as the yield type. For example:

```rust
gen fn count_to_three() -> i32 {
    yield 1;
    yield 2;
    yield 3;
}
```

In the future, I think we should experiment with a syntax like `gen fn count_to_three() yield i32 { ... }`, but that can go in another PR.

cc `@oli-obk` `@compiler-errors`
This commit is contained in:
bors 2023-12-05 18:37:15 +00:00
commit 56278a6e28
36 changed files with 417 additions and 234 deletions

View file

@ -562,6 +562,13 @@ 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

@ -10,7 +10,7 @@ use super::{
use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath;
use ast::mut_visit::{noop_visit_expr, MutVisitor};
use ast::{GenBlockKind, Pat, Path, PathSegment};
use ast::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment};
use core::mem;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@ -21,7 +21,7 @@ use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
use rustc_ast::visit::Visitor;
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
use rustc_ast_pretty::pprust;
use rustc_data_structures::stack::ensure_sufficient_stack;
@ -2237,7 +2237,7 @@ impl<'a> Parser<'a> {
let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() {
self.parse_asyncness(Case::Sensitive)
} else {
Async::No
None
};
let capture_clause = self.parse_capture_clause()?;
@ -2261,7 +2261,7 @@ impl<'a> Parser<'a> {
}
};
if let Async::Yes { span, .. } = asyncness {
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
// Feature-gate `async ||` closures.
self.sess.gated_spans.gate(sym::async_closure, span);
}
@ -2284,7 +2284,7 @@ impl<'a> Parser<'a> {
binder,
capture_clause,
constness,
asyncness,
coro_kind: asyncness,
movability,
fn_decl,
body,

View file

@ -11,8 +11,8 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast::MacCall;
use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
use rustc_ast::{Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData};
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust;
@ -2401,7 +2401,7 @@ impl<'a> Parser<'a> {
let ext_start_sp = self.token.span;
let ext = self.parse_extern(case);
if let Async::Yes { span, .. } = asyncness {
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
if span.is_rust_2015() {
self.sess.emit_err(errors::AsyncFnIn2015 {
span,
@ -2410,8 +2410,16 @@ impl<'a> Parser<'a> {
}
}
if let Gen::Yes { span, .. } = genness {
self.sess.emit_err(errors::GenFn { span });
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) });
}
if !self.eat_keyword_case(kw::Fn, case) {
@ -2444,13 +2452,18 @@ impl<'a> Parser<'a> {
}
} else if self.check_keyword(kw::Async) {
match asyncness {
Async::Yes { span, .. } => Some(WrongKw::Duplicated(span)),
Async::No => {
recover_asyncness = Async::Yes {
Some(CoroutineKind::Async { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::Gen { .. }) => {
panic!("not sure how to recover here")
}
None => {
recover_asyncness = Some(CoroutineKind::Async {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
};
});
Some(WrongKw::Misplaced(unsafe_start_sp))
}
}
@ -2531,6 +2544,8 @@ impl<'a> Parser<'a> {
}
}
// FIXME(gen_blocks): add keyword recovery logic for genness
if wrong_kw.is_some()
&& self.may_recover()
&& self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case))
@ -2542,7 +2557,7 @@ impl<'a> Parser<'a> {
return Ok(FnHeader {
constness: recover_constness,
unsafety: recover_unsafety,
asyncness: recover_asyncness,
coro_kind: recover_asyncness,
ext,
});
}
@ -2552,7 +2567,13 @@ impl<'a> Parser<'a> {
}
}
Ok(FnHeader { constness, unsafety, asyncness, ext })
let coro_kind = match asyncness {
Some(CoroutineKind::Async { .. }) => asyncness,
Some(CoroutineKind::Gen { .. }) => unreachable!("asycness cannot be Gen"),
None => genness,
};
Ok(FnHeader { constness, unsafety, coro_kind, ext })
}
/// Parses the parameter list and result type of a function declaration.

View file

@ -11,7 +11,6 @@ mod stmt;
mod ty;
use crate::lexer::UnmatchedDelim;
use ast::Gen;
pub use attr_wrapper::AttrWrapper;
pub use diagnostics::AttemptLocalParseRecovery;
pub(crate) use expr::ForbiddenLetReason;
@ -25,9 +24,10 @@ use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing};
use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::util::case::Case;
use rustc_ast::AttrId;
use rustc_ast::CoroutineKind;
use rustc_ast::DUMMY_NODE_ID;
use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern};
use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit};
use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit};
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
@ -1125,22 +1125,30 @@ impl<'a> Parser<'a> {
}
/// Parses asyncness: `async` or nothing.
fn parse_asyncness(&mut self, case: Case) -> Async {
fn parse_asyncness(&mut self, case: Case) -> Option<CoroutineKind> {
if self.eat_keyword_case(kw::Async, case) {
let span = self.prev_token.uninterpolated_span();
Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
Some(CoroutineKind::Async {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
} else {
Async::No
None
}
}
/// Parses genness: `gen` or nothing.
fn parse_genness(&mut self, case: Case) -> Gen {
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();
Gen::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
Some(CoroutineKind::Gen {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
})
} else {
Gen::No
None
}
}

View file

@ -596,7 +596,7 @@ impl<'a> Parser<'a> {
tokens: None,
};
let span_start = self.token.span;
let ast::FnHeader { ext, unsafety, constness, asyncness } =
let ast::FnHeader { ext, unsafety, constness, coro_kind } =
self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?;
if self.may_recover() && self.token.kind == TokenKind::Lt {
self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
@ -609,9 +609,10 @@ impl<'a> Parser<'a> {
// cover it.
self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span });
}
if let ast::Async::Yes { span, .. } = asyncness {
if let Some(ast::CoroutineKind::Async { span, .. }) = coro_kind {
self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span });
}
// FIXME(gen_blocks): emit a similar error for `gen fn()`
let decl_span = span_start.to(self.token.span);
Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span })))
}