Skip to content

Add support for postfix yield expressions #138435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1657,7 +1657,7 @@ pub enum ExprKind {
Try(P<Expr>),

/// A `yield`, with an optional value to be yielded.
Yield(Option<P<Expr>>),
Yield(Option<P<Expr>>, YieldKind),

/// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
/// with an optional value to be returned.
Expand Down Expand Up @@ -1903,6 +1903,15 @@ pub enum MatchKind {
Postfix,
}

/// The kind of yield expression
#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)]
pub enum YieldKind {
/// yield expr { ... }
Prefix,
/// expr.yield { ... }
Postfix,
}

/// A literal in a meta item.
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
pub struct MetaItemLit {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1813,7 +1813,7 @@ pub fn walk_expr<T: MutVisitor>(vis: &mut T, Expr { kind, id, span, attrs, token
ExprKind::Paren(expr) => {
vis.visit_expr(expr);
}
ExprKind::Yield(expr) => {
ExprKind::Yield(expr, _) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::Try(expr) => vis.visit_expr(expr),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Range(_, Some(e), _)
| Ret(Some(e))
| Unary(_, e)
| Yield(Some(e))
| Yield(Some(e), _)
| Yeet(Some(e))
| Become(e) => {
expr = e;
Expand Down Expand Up @@ -217,7 +217,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
Break(_, None)
| Range(_, None, _)
| Ret(None)
| Yield(None)
| Yield(None, _)
| Array(_)
| Call(_, _)
| MethodCall(_)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
try_visit!(visitor.visit_ty(container));
walk_list!(visitor, visit_ident, fields.iter());
}
ExprKind::Yield(optional_expression) => {
ExprKind::Yield(optional_expression, _) => {
visit_opt!(visitor, visit_expr, optional_expression);
}
ExprKind::Try(subexpression) => try_visit!(visitor.visit_expr(subexpression)),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
rest,
)
}
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
ExprKind::Yield(opt_expr, _) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
ExprKind::Err(guar) => hir::ExprKind::Err(*guar),

ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ fn may_contain_yield_point(e: &ast::Expr) -> bool {
type Result = ControlFlow<()>;

fn visit_expr(&mut self, e: &ast::Expr) -> ControlFlow<()> {
if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_, _) = e.kind {
ControlFlow::Break(())
} else {
visit::walk_expr(self, e)
Expand Down
14 changes: 12 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
use rustc_ast::{
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
FormatDebugHex, FormatSign, FormatTrait, token,
FormatDebugHex, FormatSign, FormatTrait, YieldKind, token,
};

use crate::pp::Breaks::Inconsistent;
Expand Down Expand Up @@ -761,7 +761,7 @@ impl<'a> State<'a> {
self.print_expr(e, FixupContext::default());
self.pclose();
}
ast::ExprKind::Yield(e) => {
ast::ExprKind::Yield(e, YieldKind::Prefix) => {
self.word("yield");

if let Some(expr) = e {
Expand All @@ -773,6 +773,16 @@ impl<'a> State<'a> {
);
}
}
ast::ExprKind::Yield(e, YieldKind::Postfix) => {
// it's not possible to have a postfix yield with no expression.
let e = e.as_ref().unwrap();
self.print_expr_cond_paren(
e,
e.precedence() < ExprPrecedence::Unambiguous,
fixup.leftmost_subexpression_with_dot(),
);
self.word(".yield");
}
ast::ExprKind::Try(e) => {
self.print_expr_cond_paren(
e,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::While(_, _, _)
| ExprKind::Yeet(_)
| ExprKind::Become(_)
| ExprKind::Yield(_)
| ExprKind::Yield(_, _)
| ExprKind::UnsafeBinderCast(..) => {}
}
}
Expand Down
14 changes: 12 additions & 2 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use rustc_ast::{
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
YieldKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::stack::ensure_sufficient_stack;
Expand Down Expand Up @@ -1310,6 +1311,15 @@ impl<'a> Parser<'a> {
return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix);
}

// Post-fix yield
if self.eat_keyword(exp!(Yield)) {
let yield_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::yield_expr, yield_span);
return Ok(
self.mk_expr(yield_span, ExprKind::Yield(Some(self_arg), YieldKind::Postfix))
);
}

let fn_span_lo = self.token.span;
let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;
self.check_trailing_angle_brackets(&seg, &[exp!(OpenParen)]);
Expand Down Expand Up @@ -1884,7 +1894,7 @@ impl<'a> Parser<'a> {
/// Parse `"yield" expr?`.
fn parse_expr_yield(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let kind = ExprKind::Yield(self.parse_expr_opt()?);
let kind = ExprKind::Yield(self.parse_expr_opt()?, YieldKind::Prefix);
let span = lo.to(self.prev_token.span);
self.psess.gated_spans.gate(sym::yield_expr, span);
let expr = self.mk_expr(span, kind);
Expand Down Expand Up @@ -4038,7 +4048,7 @@ impl MutVisitor for CondChecker<'_> {
| ExprKind::MacCall(_)
| ExprKind::Struct(_)
| ExprKind::Repeat(_, _)
| ExprKind::Yield(_)
| ExprKind::Yield(_, _)
| ExprKind::Yeet(_)
| ExprKind::Become(_)
| ExprKind::IncludedBytes(_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ fn ident_difference_expr_with_base_location(
&strip_non_ident_wrappers(left).kind,
&strip_non_ident_wrappers(right).kind,
) {
(Yield(_), Yield(_))
(Yield(_, _), Yield(_, _))
| (Try(_), Try(_))
| (Paren(_), Paren(_))
| (Repeat(_, _), Repeat(_, _))
Expand Down
5 changes: 3 additions & 2 deletions src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt),
(Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb),
(TryBlock(l), TryBlock(r)) => eq_block(l, r),
(Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()),
(Yield(l, lk), Yield(r, rk)) => eq_expr_opt(l.as_ref(), r.as_ref()) && lk == rk,
(Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()),
(Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()),
(Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()),
(Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => {
Expand Down Expand Up @@ -688,7 +689,7 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool {

pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
use WherePredicateKind::*;
over(&l.attrs, &r.attrs, eq_attr)
over(&l.attrs, &r.attrs, eq_attr)
&& match (&l.kind, &r.kind) {
(BoundPredicate(l), BoundPredicate(r)) => {
over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
Expand Down
5 changes: 3 additions & 2 deletions src/tools/rustfmt/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ pub(crate) fn format_expr(
Ok(format!("break{id_str}"))
}
}
ast::ExprKind::Yield(ref opt_expr) => {
ast::ExprKind::Yield(ref opt_expr, ast::YieldKind::Prefix) => {
if let Some(ref expr) = *opt_expr {
rewrite_unary_prefix(context, "yield ", &**expr, shape)
} else {
Expand All @@ -243,7 +243,8 @@ pub(crate) fn format_expr(
ast::ExprKind::Try(..)
| ast::ExprKind::Field(..)
| ast::ExprKind::MethodCall(..)
| ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape),
| ast::ExprKind::Await(_, _)
| ast::ExprKind::Yield(_, ast::YieldKind::Postfix) => rewrite_chain(expr, context, shape),
ast::ExprKind::MacCall(ref mac) => {
rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| {
wrap_str(
Expand Down
4 changes: 2 additions & 2 deletions src/tools/rustfmt/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::Index(_, ref expr, _)
| ast::ExprKind::Unary(_, ref expr)
| ast::ExprKind::Try(ref expr)
| ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr),
| ast::ExprKind::Yield(Some(ref expr), _) => is_block_expr(context, expr, repr),
ast::ExprKind::Closure(ref closure) => is_block_expr(context, &closure.body, repr),
// This can only be a string lit
ast::ExprKind::Lit(_) => {
Expand Down Expand Up @@ -515,7 +515,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
| ast::ExprKind::Tup(..)
| ast::ExprKind::Use(..)
| ast::ExprKind::Type(..)
| ast::ExprKind::Yield(None)
| ast::ExprKind::Yield(None, _)
| ast::ExprKind::Underscore => false,
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/tools/rustfmt/tests/source/postfix-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This demonstrates a proposed alternate or additional option of having yield in postfix position.
//@ edition: 2024

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

use std::ops::{Coroutine, CoroutineState};
use std::pin::pin;

fn main() {
let mut coro =
pin!(#[coroutine] |_: i32| { let x = 1.yield;


(x + 2).yield; });
}
12 changes: 12 additions & 0 deletions src/tools/rustfmt/tests/target/postfix-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// This demonstrates a proposed alternate or additional option of having yield in postfix position.
//@ edition: 2024

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

use std::ops::{Coroutine, CoroutineState};
use std::pin::pin;

fn main() {
let mut coro =
pin!(#[coroutine] |_: i32| { let x = 1.yield; (x + 2).yield; });
}
15 changes: 15 additions & 0 deletions tests/pretty/postfix-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This demonstrates a proposed alternate or additional option of having yield in postfix position.
//@ edition: 2024
//@ pp-exact

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

use std::ops::{Coroutine, CoroutineState};
use std::pin::pin;

fn main() {
let mut gn = gen { yield 1; 2.yield; (1 + 2).yield; };

let mut coro =
pin!(#[coroutine] |_: i32| { let x = 1.yield; (x + 2).yield; });
}
34 changes: 34 additions & 0 deletions tests/ui/coroutine/postfix-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// This demonstrates a proposed alternate or additional option of having yield in postfix position.

//@ run-pass
//@ edition: 2024

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

use std::ops::{Coroutine, CoroutineState};
use std::pin::pin;

fn main() {
// generators (i.e. yield doesn't return anything useful)
let mut gn = gen {
yield 1;
2.yield;
};

assert_eq!(gn.next(), Some(1));
assert_eq!(gn.next(), Some(2));
assert_eq!(gn.next(), None);

//coroutines (i.e. yield returns something useful)
let mut coro = pin!(
#[coroutine]
|_: i32| {
let x = 1.yield;
(x + 2).yield;
}
);

assert_eq!(coro.as_mut().resume(0), CoroutineState::Yielded(1));
assert_eq!(coro.as_mut().resume(2), CoroutineState::Yielded(4));
assert_eq!(coro.as_mut().resume(3), CoroutineState::Complete(()));
}
Loading