Skip to content

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

+32-6
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

+1-1
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

+1
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

+1-1
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

+2-2
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

+1-1
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

+76-6
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

+6-1
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

+2-2
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

+2-1
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

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -2491,11 +2491,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
24912491

24922492
let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
24932493
Ok(string) => {
2494-
if string.starts_with("async ") {
2495-
let pos = args_span.lo() + BytePos(6);
2496-
(args_span.with_lo(pos).with_hi(pos), "move ")
2497-
} else if string.starts_with("async|") {
2498-
let pos = args_span.lo() + BytePos(5);
2494+
let coro_prefix = if string.starts_with("async") {
2495+
// `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32`
2496+
Some(5)
2497+
} else if string.starts_with("gen") {
2498+
// `gen` is 3 chars long
2499+
Some(3)
2500+
} else {
2501+
None
2502+
};
2503+
if let Some(n) = coro_prefix {
2504+
let pos = args_span.lo() + BytePos(n);
24992505
(args_span.with_lo(pos).with_hi(pos), " move")
25002506
} else {
25012507
(args_span.shrink_to_lo(), "move ")
@@ -2505,6 +2511,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
25052511
};
25062512
let kind = match use_span.coroutine_kind() {
25072513
Some(coroutine_kind) => match coroutine_kind {
2514+
CoroutineKind::Gen(kind) => match kind {
2515+
CoroutineSource::Block => "gen block",
2516+
CoroutineSource::Closure => "gen closure",
2517+
_ => bug!("gen block/closure expected, but gen function found."),
2518+
},
25082519
CoroutineKind::Async(async_kind) => match async_kind {
25092520
CoroutineSource::Block => "async block",
25102521
CoroutineSource::Closure => "async closure",

compiler/rustc_borrowck/src/diagnostics/region_name.rs

+14
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,20 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
698698
" of async function"
699699
}
700700
},
701+
Some(hir::CoroutineKind::Gen(gen)) => match gen {
702+
hir::CoroutineSource::Block => " of gen block",
703+
hir::CoroutineSource::Closure => " of gen closure",
704+
hir::CoroutineSource::Fn => {
705+
let parent_item =
706+
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
707+
let output = &parent_item
708+
.fn_decl()
709+
.expect("coroutine lowered from gen fn should be in fn")
710+
.output;
711+
span = output.span();
712+
" of gen function"
713+
}
714+
},
701715
Some(hir::CoroutineKind::Coroutine) => " of coroutine",
702716
None => " of closure",
703717
};

compiler/rustc_builtin_macros/src/assert/context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
294294
// sync with the `rfc-2011-nicer-assert-messages/all-expr-kinds.rs` test.
295295
ExprKind::Assign(_, _, _)
296296
| ExprKind::AssignOp(_, _, _)
297-
| ExprKind::Async(_, _)
297+
| ExprKind::Gen(_, _, _)
298298
| ExprKind::Await(_, _)
299299
| ExprKind::Block(_, _)
300300
| ExprKind::Break(_, _)

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+3
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,9 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &
560560

561561
fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
562562
match coroutine_kind {
563+
Some(CoroutineKind::Gen(CoroutineSource::Block)) => "gen_block",
564+
Some(CoroutineKind::Gen(CoroutineSource::Closure)) => "gen_closure",
565+
Some(CoroutineKind::Gen(CoroutineSource::Fn)) => "gen_fn",
563566
Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block",
564567
Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure",
565568
Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn",

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ declare_features! (
456456
(unstable, ffi_returns_twice, "1.34.0", Some(58314), None),
457457
/// Allows using `#[repr(align(...))]` on function items
458458
(unstable, fn_align, "1.53.0", Some(82232), None),
459+
/// Allows defining gen blocks and `gen fn`.
460+
(unstable, gen_blocks, "CURRENT_RUSTC_VERSION", Some(117078), None),
459461
/// Infer generic args for both consts and types.
460462
(unstable, generic_arg_infer, "1.55.0", Some(85077), None),
461463
/// An extension to the `generic_associated_types` feature, allowing incomplete features.

0 commit comments

Comments
 (0)