Skip to content

Commit 59096cd

Browse files
committed
Auto merge of rust-lang#119061 - compiler-errors:async-gen-abi, r=wesleywiser
Desugar `yield` in `async gen` correctly, ensure `gen` always returns unit 1. Ensure `async gen` blocks desugar `yield $expr` to `task_context = yield async_gen_ready($expr)`. Previously we were not assigning the `task_context` correctly, meaning that `yield` expressions in async generators returned type `ResumeTy` instead of `()`, and that we were not storing the `task_context` (which is probably unsound if we were reading the old task-context which has an invalidated borrow or something...) 2. Ensure that all `(async?) gen` blocks and `(async?) gen` fns return unit. Previously we were only checking this for `gen fn`, meaning that `gen {}` and `async gen {}` and `async gen fn` were allowed to return values that weren't unit. This is why rust-lang#119058 was an ICE rather than an E0308. Fixes rust-lang#119058.
2 parents e719b6f + 0f10acf commit 59096cd

File tree

6 files changed

+125
-23
lines changed

6 files changed

+125
-23
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+32-20
Original file line numberDiff line numberDiff line change
@@ -917,12 +917,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
917917
let poll_expr = {
918918
let awaitee = self.expr_ident(span, awaitee_ident, awaitee_pat_hid);
919919
let ref_mut_awaitee = self.expr_mut_addr_of(span, awaitee);
920-
let task_context = if let Some(task_context_hid) = self.task_context {
921-
self.expr_ident_mut(span, task_context_ident, task_context_hid)
922-
} else {
923-
// Use of `await` outside of an async context, we cannot use `task_context` here.
924-
self.expr_err(span, self.tcx.sess.span_delayed_bug(span, "no task_context hir id"))
920+
921+
let Some(task_context_hid) = self.task_context else {
922+
unreachable!("use of `await` outside of an async context.");
925923
};
924+
925+
let task_context = self.expr_ident_mut(span, task_context_ident, task_context_hid);
926+
926927
let new_unchecked = self.expr_call_lang_item_fn_mut(
927928
span,
928929
hir::LangItem::PinNewUnchecked,
@@ -991,16 +992,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
991992
);
992993
let yield_expr = self.arena.alloc(yield_expr);
993994

994-
if let Some(task_context_hid) = self.task_context {
995-
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
996-
let assign =
997-
self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
998-
self.stmt_expr(span, assign)
999-
} else {
1000-
// Use of `await` outside of an async context. Return `yield_expr` so that we can
1001-
// proceed with type checking.
1002-
self.stmt(span, hir::StmtKind::Semi(yield_expr))
1003-
}
995+
let Some(task_context_hid) = self.task_context else {
996+
unreachable!("use of `await` outside of an async context.");
997+
};
998+
999+
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
1000+
let assign =
1001+
self.expr(span, hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span)));
1002+
self.stmt_expr(span, assign)
10041003
};
10051004

10061005
let loop_block = self.block_all(span, arena_vec![self; inner_match_stmt, yield_stmt], None);
@@ -1635,19 +1634,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
16351634
}
16361635
};
16371636

1638-
let mut yielded =
1637+
let yielded =
16391638
opt_expr.as_ref().map(|x| self.lower_expr(x)).unwrap_or_else(|| self.expr_unit(span));
16401639

16411640
if is_async_gen {
1642-
// yield async_gen_ready($expr);
1643-
yielded = self.expr_call_lang_item_fn(
1641+
// `yield $expr` is transformed into `task_context = yield async_gen_ready($expr)`.
1642+
// This ensures that we store our resumed `ResumeContext` correctly, and also that
1643+
// the apparent value of the `yield` expression is `()`.
1644+
let wrapped_yielded = self.expr_call_lang_item_fn(
16441645
span,
16451646
hir::LangItem::AsyncGenReady,
16461647
std::slice::from_ref(yielded),
16471648
);
1648-
}
1649+
let yield_expr = self.arena.alloc(
1650+
self.expr(span, hir::ExprKind::Yield(wrapped_yielded, hir::YieldSource::Yield)),
1651+
);
16491652

1650-
hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
1653+
let Some(task_context_hid) = self.task_context else {
1654+
unreachable!("use of `await` outside of an async context.");
1655+
};
1656+
let task_context_ident = Ident::with_dummy_span(sym::_task_context);
1657+
let lhs = self.expr_ident(span, task_context_ident, task_context_hid);
1658+
1659+
hir::ExprKind::Assign(lhs, yield_expr, self.lower_span(span))
1660+
} else {
1661+
hir::ExprKind::Yield(yielded, hir::YieldSource::Yield)
1662+
}
16511663
}
16521664

16531665
/// Desugar `ExprForLoop` from: `[opt_ident]: for <pat> in <head> <body>` into:

compiler/rustc_hir_typeck/src/closure.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
650650
},
651651
)
652652
}
653-
// For a `gen {}` block created as a `gen fn` body, we need the return type to be
654-
// ().
655-
Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => self.tcx.types.unit,
653+
// All `gen {}` and `async gen {}` must return unit.
654+
Some(hir::CoroutineKind::Gen(_) | hir::CoroutineKind::AsyncGen(_)) => {
655+
self.tcx.types.unit
656+
}
656657

657658
_ => astconv.ty_infer(None, decl.output.span()),
658659
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: --edition 2024 -Zunstable-options
2+
// check-pass
3+
4+
#![feature(async_iterator, gen_blocks, noop_waker)]
5+
6+
use std::{async_iter::AsyncIterator, pin::pin, task::{Context, Waker}};
7+
8+
async gen fn gen_fn() -> &'static str {
9+
yield "hello"
10+
}
11+
12+
pub fn main() {
13+
let async_iterator = pin!(gen_fn());
14+
let waker = Waker::noop();
15+
let ctx = &mut Context::from_waker(&waker);
16+
async_iterator.poll_next(ctx);
17+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// compile-flags: --edition 2024 -Zunstable-options
2+
// check-pass
3+
4+
#![feature(gen_blocks)]
5+
6+
fn diverge() -> ! { loop {} }
7+
8+
async gen fn async_gen_fn() -> i32 { diverge() }
9+
10+
gen fn gen_fn() -> i32 { diverge() }
11+
12+
fn async_gen_block() {
13+
async gen { yield (); diverge() };
14+
}
15+
16+
fn gen_block() {
17+
gen { yield (); diverge() };
18+
}
19+
20+
fn main() {}

tests/ui/coroutine/return-types.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-flags: --edition 2024 -Zunstable-options
2+
3+
#![feature(gen_blocks)]
4+
5+
async gen fn async_gen_fn() -> i32 { 0 }
6+
//~^ ERROR mismatched types
7+
8+
gen fn gen_fn() -> i32 { 0 }
9+
//~^ ERROR mismatched types
10+
11+
fn async_gen_block() {
12+
async gen { yield (); 1 };
13+
//~^ ERROR mismatched types
14+
}
15+
16+
fn gen_block() {
17+
gen { yield (); 1 };
18+
//~^ ERROR mismatched types
19+
}
20+
21+
fn main() {}
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/return-types.rs:5:38
3+
|
4+
LL | async gen fn async_gen_fn() -> i32 { 0 }
5+
| --- ^ expected `()`, found integer
6+
| |
7+
| expected `()` because of return type
8+
9+
error[E0308]: mismatched types
10+
--> $DIR/return-types.rs:8:26
11+
|
12+
LL | gen fn gen_fn() -> i32 { 0 }
13+
| --- ^ expected `()`, found integer
14+
| |
15+
| expected `()` because of return type
16+
17+
error[E0308]: mismatched types
18+
--> $DIR/return-types.rs:12:27
19+
|
20+
LL | async gen { yield (); 1 };
21+
| ^ expected `()`, found integer
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/return-types.rs:17:21
25+
|
26+
LL | gen { yield (); 1 };
27+
| ^ expected `()`, found integer
28+
29+
error: aborting due to 4 previous errors
30+
31+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)