Skip to content

Commit

Permalink
Add explicit syntax for coroutines instead of relying on closures hav…
Browse files Browse the repository at this point in the history
…ing `yield` expressions
  • Loading branch information
oli-obk committed Apr 11, 2024
1 parent 05ccc49 commit e8a6ccc
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 24 deletions.
19 changes: 15 additions & 4 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,18 +201,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_decl_span,
fn_arg_span,
}) => match coroutine_kind {
Some(coroutine_kind) => self.lower_expr_coroutine_closure(
Some(kind @ CoroutineKind::Async { .. }) => self.lower_expr_coroutine_closure(
binder,
*capture_clause,
e.id,
hir_id,
*coroutine_kind,
*kind,
fn_decl,
body,
*fn_decl_span,
*fn_arg_span,
),
None => self.lower_expr_closure(
_ => self.lower_expr_closure(
binder,
*capture_clause,
e.id,
Expand All @@ -222,6 +222,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
body,
*fn_decl_span,
*fn_arg_span,
*coroutine_kind,
),
},
ExprKind::Gen(capture_clause, block, genblock_kind) => {
Expand Down Expand Up @@ -963,12 +964,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
body: &Expr,
fn_decl_span: Span,
fn_arg_span: Span,
coroutine_kind: Option<CoroutineKind>,
) -> hir::ExprKind<'hir> {
let (binder_clause, generic_params) = self.lower_closure_binder(binder);

let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| {
let mut coroutine_kind = None;
let mut coroutine_kind = coroutine_kind.map(|k| match k {
CoroutineKind::Async { span, .. } => {
span_bug!(span, "should have used lower_expr_coroutine_closure")
}
CoroutineKind::Gen { .. } => hir::CoroutineKind::Coroutine(Movability::Movable),
CoroutineKind::AsyncGen { span, .. } => {
span_bug!(span, "async gen closures are not supported yet")
}
});
let body_id = this.lower_fn_body(decl, |this| {
this.coroutine_kind = coroutine_kind;
let e = this.lower_expr_mut(body);
coroutine_kind = this.coroutine_kind;
e
Expand Down
21 changes: 11 additions & 10 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1522,17 +1522,18 @@ impl<'a> Parser<'a> {
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.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) {
if this.check_keyword(kw::Async) {
// FIXME(gen_blocks): Parse `gen async` and suggest swap
if this.is_gen_block(kw::Async, 0) {
if this.is_gen_block(kw::Async, 0) || this.is_gen_block(kw::Gen, 1) {
// Check for `async {` and `async move {`,
this.parse_gen_block()
} else {
this.parse_expr_closure()
}
} else if this.check_keyword(kw::Gen)
&& this.token.uninterpolated_span().at_least_rust_2024()
{
if this.is_gen_block(kw::Gen, 0) {
// Check for `async {` and `async move {`,
this.parse_gen_block()
} else {
Expand Down
10 changes: 6 additions & 4 deletions tests/ui/coroutine/addassign-yield.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
//@ run-pass
//@ edition: 2024
//@ compile-flags: -Zunstable-options
// Regression test for broken MIR error (#61442)
// Due to the two possible evaluation orders for
// a '+=' expression (depending on whether or not the 'AddAssign' trait
// is being used), we were failing to account for all types that might
// possibly be live across a yield point.

#![feature(coroutines)]
#![feature(coroutines, gen_blocks)]

fn foo() {
let _x = static || {
let _x = static gen || {
let mut s = String::new();
s += { yield; "" };
};

let _y = static || {
let _y = static gen || {
let x = &mut 0;
*{ yield; x } += match String::new() { _ => 0 };
};

// Please don't ever actually write something like this
let _z = static || {
let _z = static gen || {
let x = &mut 0;
*{
let inner = &mut 1;
Expand Down
6 changes: 4 additions & 2 deletions tests/ui/coroutine/borrow-in-tail-expr.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//@ run-pass
//@ edition: 2024
//@ compile-flags: -Zunstable-options

#![feature(coroutines)]
#![feature(coroutines, gen_blocks)]

fn main() {
let _a = || {
let _a = gen || {
yield;
let a = String::new();
a.len()
Expand Down
10 changes: 6 additions & 4 deletions tests/ui/coroutine/derived-drop-parent-expr.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
//@ build-pass
//@ edition: 2024
//@ compile-flags: -Zunstable-options

//! Like drop-tracking-parent-expression, but also tests that this doesn't ICE when building MIR
#![feature(coroutines)]
#![feature(coroutines, gen_blocks)]

fn assert_send<T: Send>(_thing: T) {}

#[derive(Default)]
pub struct Client { pub nickname: String }

fn main() {
let g = move || match drop(Client { ..Client::default() }) {
_status => yield,
};
let g = gen || match drop(Client { ..Client::default() }) {
_status => yield,
};
assert_send(g);
}

0 comments on commit e8a6ccc

Please sign in to comment.