1
Fork 0

Auto merge of #116447 - oli-obk:gen_fn, r=compiler-errors

Implement `gen` blocks in the 2024 edition

Coroutines tracking issue https://github.com/rust-lang/rust/issues/43122
`gen` block tracking issue https://github.com/rust-lang/rust/issues/117078

This PR implements `gen` blocks that implement `Iterator`. Most of the logic with `async` blocks is shared, and thus I renamed various types that were referring to `async` specifically.

An example usage of `gen` blocks is

```rust
fn foo() -> impl Iterator<Item = i32> {
    gen {
        yield 42;
        for i in 5..18 {
            if i.is_even() { continue }
            yield i * 2;
        }
    }
}
```

The limitations (to be resolved) of the implementation are listed in the tracking issue
This commit is contained in:
bors 2023-10-29 00:03:52 +00:00
commit 2cad938a81
75 changed files with 1096 additions and 148 deletions

View file

@ -278,6 +278,9 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}`
parse_function_body_equals_expr = function body cannot be `= expression;`
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
parse_gen_block = `gen` blocks are not yet implemented
.help = only the keyword is reserved for now
parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax
parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets

View file

@ -520,6 +520,14 @@ pub(crate) struct CatchAfterTry {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_gen_block)]
#[help]
pub(crate) struct GenBlock {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_comma_after_base_struct)]
#[note]

View file

@ -9,7 +9,7 @@ use super::{
use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath;
use ast::mut_visit::{noop_visit_expr, MutVisitor};
use ast::{Path, PathSegment};
use ast::{GenBlockKind, Path, PathSegment};
use core::mem;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
@ -1441,14 +1441,20 @@ 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_async_block() {
if this.is_gen_block(kw::Async) {
// Check for `async {` and `async move {`.
this.parse_async_block()
this.parse_gen_block()
} else {
this.parse_expr_closure()
}
} else if this.eat_keyword(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) {
this.parse_gen_block()
} else {
this.parse_expr_lit()
}
} else {
this.parse_expr_lit()
}
@ -1848,7 +1854,7 @@ impl<'a> Parser<'a> {
let lo = self.prev_token.span;
let kind = ExprKind::Yield(self.parse_expr_opt()?);
let span = lo.to(self.prev_token.span);
self.sess.gated_spans.gate(sym::coroutines, span);
self.sess.gated_spans.gate(sym::yield_expr, span);
let expr = self.mk_expr(span, kind);
self.maybe_recover_from_bad_qpath(expr)
}
@ -3059,18 +3065,24 @@ impl<'a> Parser<'a> {
&& self.token.uninterpolated_span().at_least_rust_2018()
}
/// Parses an `async move? {...}` expression.
fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> {
/// Parses an `async move? {...}` or `gen move? {...}` expression.
fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.expect_keyword(kw::Async)?;
let kind = if self.eat_keyword(kw::Async) {
GenBlockKind::Async
} else {
assert!(self.eat_keyword(kw::Gen));
self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span));
GenBlockKind::Gen
};
let capture_clause = self.parse_capture_clause()?;
let (attrs, body) = self.parse_inner_attrs_and_block()?;
let kind = ExprKind::Async(capture_clause, body);
let kind = ExprKind::Gen(capture_clause, body, kind);
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
}
fn is_async_block(&self) -> bool {
self.token.is_keyword(kw::Async)
fn is_gen_block(&self, kw: Symbol) -> bool {
self.token.is_keyword(kw)
&& ((
// `async move {`
self.is_keyword_ahead(1, &[kw::Move])
@ -3596,7 +3608,7 @@ impl MutVisitor for CondChecker<'_> {
| ExprKind::Match(_, _)
| ExprKind::Closure(_)
| ExprKind::Block(_, _)
| ExprKind::Async(_, _)
| ExprKind::Gen(_, _, _)
| ExprKind::TryBlock(_)
| ExprKind::Underscore
| ExprKind::Path(_, _)

View file

@ -2297,9 +2297,9 @@ impl<'a> Parser<'a> {
// `pub` is added in case users got confused with the ordering like `async pub fn`,
// only if it wasn't preceded by `default` as `default pub` is invalid.
let quals: &[Symbol] = if check_pub {
&[kw::Pub, kw::Const, kw::Async, kw::Unsafe, kw::Extern]
&[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern]
} else {
&[kw::Const, kw::Async, kw::Unsafe, kw::Extern]
&[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern]
};
self.check_keyword_case(kw::Fn, case) // Definitely an `fn`.
// `$qual fn` or `$qual $qual`:
@ -2353,6 +2353,9 @@ impl<'a> Parser<'a> {
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 unsafe_start_sp = self.token.span;
let unsafety = self.parse_unsafety(case);
@ -2368,6 +2371,10 @@ impl<'a> Parser<'a> {
}
}
if let Gen::Yes { span, .. } = genness {
self.sess.emit_err(errors::GenBlock { span });
}
if !self.eat_keyword_case(kw::Fn, case) {
// It is possible for `expect_one_of` to recover given the contents of
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't

View file

@ -11,6 +11,7 @@ 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;
@ -1128,6 +1129,16 @@ impl<'a> Parser<'a> {
}
}
/// Parses genness: `gen` or nothing.
fn parse_genness(&mut self, case: Case) -> Gen {
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 }
} else {
Gen::No
}
}
/// Parses unsafety: `unsafe` or nothing.
fn parse_unsafety(&mut self, case: Case) -> Unsafe {
if self.eat_keyword_case(kw::Unsafe, case) {