Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 2cad938

Browse files
committed
Auto merge of rust-lang#116447 - oli-obk:gen_fn, r=compiler-errors
Implement `gen` blocks in the 2024 edition Coroutines tracking issue rust-lang#43122 `gen` block tracking issue rust-lang#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
2 parents e5cfc55 + eb66d10 commit 2cad938

File tree

75 files changed

+1096
-148
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1096
-148
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,7 @@ impl Expr {
12351235
ExprKind::Closure(..) => ExprPrecedence::Closure,
12361236
ExprKind::Block(..) => ExprPrecedence::Block,
12371237
ExprKind::TryBlock(..) => ExprPrecedence::TryBlock,
1238-
ExprKind::Async(..) => ExprPrecedence::Async,
1238+
ExprKind::Gen(..) => ExprPrecedence::Gen,
12391239
ExprKind::Await(..) => ExprPrecedence::Await,
12401240
ExprKind::Assign(..) => ExprPrecedence::Assign,
12411241
ExprKind::AssignOp(..) => ExprPrecedence::AssignOp,
@@ -1405,11 +1405,9 @@ pub enum ExprKind {
14051405
Closure(Box<Closure>),
14061406
/// A block (`'label: { ... }`).
14071407
Block(P<Block>, Option<Label>),
1408-
/// An async block (`async move { ... }`).
1409-
///
1410-
/// The async block used to have a `NodeId`, which was removed in favor of
1411-
/// using the parent `NodeId` of the parent `Expr`.
1412-
Async(CaptureBy, P<Block>),
1408+
/// An `async` block (`async move { ... }`),
1409+
/// or a `gen` block (`gen move { ... }`)
1410+
Gen(CaptureBy, P<Block>, GenBlockKind),
14131411
/// An await expression (`my_future.await`). Span is of await keyword.
14141412
Await(P<Expr>, Span),
14151413

@@ -1499,6 +1497,28 @@ pub enum ExprKind {
14991497
Err,
15001498
}
15011499

1500+
/// Used to differentiate between `async {}` blocks and `gen {}` blocks.
1501+
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
1502+
pub enum GenBlockKind {
1503+
Async,
1504+
Gen,
1505+
}
1506+
1507+
impl fmt::Display for GenBlockKind {
1508+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1509+
self.modifier().fmt(f)
1510+
}
1511+
}
1512+
1513+
impl GenBlockKind {
1514+
pub fn modifier(&self) -> &'static str {
1515+
match self {
1516+
GenBlockKind::Async => "async",
1517+
GenBlockKind::Gen => "gen",
1518+
}
1519+
}
1520+
}
1521+
15021522
/// The explicit `Self` type in a "qualified path". The actual
15031523
/// path, including the trait and the associated item, is stored
15041524
/// separately. `position` represents the index of the associated
@@ -2363,6 +2383,12 @@ pub enum Async {
23632383
No,
23642384
}
23652385

2386+
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
2387+
pub enum Gen {
2388+
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
2389+
No,
2390+
}
2391+
23662392
impl Async {
23672393
pub fn is_async(self) -> bool {
23682394
matches!(self, Async::Yes { .. })

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
14181418
vis.visit_block(blk);
14191419
visit_opt(label, |label| vis.visit_label(label));
14201420
}
1421-
ExprKind::Async(_capture_by, body) => {
1421+
ExprKind::Gen(_capture_by, body, _) => {
14221422
vis.visit_block(body);
14231423
}
14241424
ExprKind::Await(expr, await_kw_span) => {

compiler/rustc_ast/src/token.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
197197
kw::Continue,
198198
kw::False,
199199
kw::For,
200+
kw::Gen,
200201
kw::If,
201202
kw::Let,
202203
kw::Loop,

compiler/rustc_ast/src/util/classify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
4646
Closure(closure) => {
4747
expr = &closure.body;
4848
}
49-
Async(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
49+
Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
5050
| TryBlock(..) | While(..) => break Some(expr),
5151
_ => break None,
5252
}

compiler/rustc_ast/src/util/parser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ pub enum ExprPrecedence {
285285
Block,
286286
TryBlock,
287287
Struct,
288-
Async,
288+
Gen,
289289
Await,
290290
Err,
291291
}
@@ -351,7 +351,7 @@ impl ExprPrecedence {
351351
| ExprPrecedence::ConstBlock
352352
| ExprPrecedence::Block
353353
| ExprPrecedence::TryBlock
354-
| ExprPrecedence::Async
354+
| ExprPrecedence::Gen
355355
| ExprPrecedence::Struct
356356
| ExprPrecedence::Err => PREC_PAREN,
357357
}

compiler/rustc_ast/src/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
872872
walk_list!(visitor, visit_label, opt_label);
873873
visitor.visit_block(block);
874874
}
875-
ExprKind::Async(_, body) => {
875+
ExprKind::Gen(_, body, _) => {
876876
visitor.visit_block(body);
877877
}
878878
ExprKind::Await(expr, _) => visitor.visit_expr(expr),

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
183183
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
184184
hir::MatchSource::Normal,
185185
),
186-
ExprKind::Async(capture_clause, block) => self.make_async_expr(
186+
ExprKind::Gen(capture_clause, block, GenBlockKind::Async) => self.make_async_expr(
187187
*capture_clause,
188188
e.id,
189189
None,
@@ -317,6 +317,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
317317
rest,
318318
)
319319
}
320+
ExprKind::Gen(capture_clause, block, GenBlockKind::Gen) => self.make_gen_expr(
321+
*capture_clause,
322+
e.id,
323+
None,
324+
e.span,
325+
hir::CoroutineSource::Block,
326+
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
327+
),
320328
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
321329
ExprKind::Err => hir::ExprKind::Err(
322330
self.tcx.sess.delay_span_bug(e.span, "lowered ExprKind::Err"),
@@ -661,6 +669,57 @@ impl<'hir> LoweringContext<'_, 'hir> {
661669
}))
662670
}
663671

672+
/// Lower a `gen` construct to a generator that implements `Iterator`.
673+
///
674+
/// This results in:
675+
///
676+
/// ```text
677+
/// static move? |()| -> () {
678+
/// <body>
679+
/// }
680+
/// ```
681+
pub(super) fn make_gen_expr(
682+
&mut self,
683+
capture_clause: CaptureBy,
684+
closure_node_id: NodeId,
685+
_yield_ty: Option<hir::FnRetTy<'hir>>,
686+
span: Span,
687+
gen_kind: hir::CoroutineSource,
688+
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
689+
) -> hir::ExprKind<'hir> {
690+
let output = hir::FnRetTy::DefaultReturn(self.lower_span(span));
691+
692+
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
693+
let fn_decl = self.arena.alloc(hir::FnDecl {
694+
inputs: &[],
695+
output,
696+
c_variadic: false,
697+
implicit_self: hir::ImplicitSelfKind::None,
698+
lifetime_elision_allowed: false,
699+
});
700+
701+
let body = self.lower_body(move |this| {
702+
this.coroutine_kind = Some(hir::CoroutineKind::Gen(gen_kind));
703+
704+
let res = body(this);
705+
(&[], res)
706+
});
707+
708+
// `static |()| -> () { body }`:
709+
hir::ExprKind::Closure(self.arena.alloc(hir::Closure {
710+
def_id: self.local_def_id(closure_node_id),
711+
binder: hir::ClosureBinder::Default,
712+
capture_clause,
713+
bound_generic_params: &[],
714+
fn_decl,
715+
body,
716+
fn_decl_span: self.lower_span(span),
717+
fn_arg_span: None,
718+
movability: Some(Movability::Movable),
719+
constness: hir::Constness::NotConst,
720+
}))
721+
}
722+
664723
/// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to
665724
/// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled.
666725
pub(super) fn maybe_forward_track_caller(
@@ -712,7 +771,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
712771
let full_span = expr.span.to(await_kw_span);
713772
match self.coroutine_kind {
714773
Some(hir::CoroutineKind::Async(_)) => {}
715-
Some(hir::CoroutineKind::Coroutine) | None => {
774+
Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => {
716775
self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
717776
await_kw_span,
718777
item_span: self.current_item,
@@ -936,8 +995,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
936995
}
937996
Some(movability)
938997
}
939-
Some(hir::CoroutineKind::Async(_)) => {
940-
panic!("non-`async` closure body turned `async` during lowering");
998+
Some(hir::CoroutineKind::Gen(_)) | Some(hir::CoroutineKind::Async(_)) => {
999+
panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
9411000
}
9421001
None => {
9431002
if movability == Movability::Static {
@@ -1445,11 +1504,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
14451504

14461505
fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
14471506
match self.coroutine_kind {
1448-
Some(hir::CoroutineKind::Coroutine) => {}
1507+
Some(hir::CoroutineKind::Gen(_)) => {}
14491508
Some(hir::CoroutineKind::Async(_)) => {
14501509
self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span });
14511510
}
1452-
None => self.coroutine_kind = Some(hir::CoroutineKind::Coroutine),
1511+
Some(hir::CoroutineKind::Coroutine) | None => {
1512+
if !self.tcx.features().coroutines {
1513+
rustc_session::parse::feature_err(
1514+
&self.tcx.sess.parse_sess,
1515+
sym::coroutines,
1516+
span,
1517+
"yield syntax is experimental",
1518+
)
1519+
.emit();
1520+
}
1521+
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine)
1522+
}
14531523
}
14541524

14551525
let expr =

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
554554
"consider removing `for<...>`"
555555
);
556556
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
557-
gate_all!(coroutines, "yield syntax is experimental");
557+
for &span in spans.get(&sym::yield_expr).iter().copied().flatten() {
558+
if !span.at_least_rust_2024() {
559+
gate_feature_post!(&visitor, coroutines, span, "yield syntax is experimental");
560+
}
561+
}
562+
gate_all!(gen_blocks, "gen blocks are experimental");
558563
gate_all!(raw_ref_op, "raw address of syntax is experimental");
559564
gate_all!(const_trait_impl, "const trait impls are experimental");
560565
gate_all!(

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,8 @@ impl<'a> State<'a> {
445445
self.ibox(0);
446446
self.print_block_with_attrs(blk, attrs);
447447
}
448-
ast::ExprKind::Async(capture_clause, blk) => {
449-
self.word_nbsp("async");
448+
ast::ExprKind::Gen(capture_clause, blk, kind) => {
449+
self.word_nbsp(kind.modifier());
450450
self.print_capture_clause(*capture_clause);
451451
// cbox/ibox in analogy to the `ExprKind::Block` arm above
452452
self.cbox(0);

compiler/rustc_borrowck/src/borrowck_errors.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,12 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
373373
span: Span,
374374
yield_span: Span,
375375
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
376+
let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
376377
let mut err = struct_span_err!(
377378
self,
378379
span,
379380
E0626,
380-
"borrow may still be in use when coroutine yields",
381+
"borrow may still be in use when {coroutine_kind:#} yields",
381382
);
382383
err.span_label(yield_span, "possible yield occurs here");
383384
err

0 commit comments

Comments
 (0)