diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1b831c454e6d5..5b7545b339663 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1657,7 +1657,7 @@ pub enum ExprKind { Try(P), /// A `yield`, with an optional value to be yielded. - Yield(Option>), + Yield(YieldKind), /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever), /// with an optional value to be returned. @@ -1903,6 +1903,44 @@ pub enum MatchKind { Postfix, } +/// The kind of yield expression +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum YieldKind { + /// yield expr { ... } + Prefix(Option>), + /// expr.yield { ... } + Postfix(P), +} + +impl YieldKind { + /// Returns the expression inside the yield expression, if any. + /// + /// For postfix yields, this is guaranteed to be `Some`. + pub const fn expr(&self) -> Option<&P> { + match self { + YieldKind::Prefix(expr) => expr.as_ref(), + YieldKind::Postfix(expr) => Some(expr), + } + } + + /// Returns a mutable reference to the expression being yielded, if any. + pub const fn expr_mut(&mut self) -> Option<&mut P> { + match self { + YieldKind::Prefix(expr) => expr.as_mut(), + YieldKind::Postfix(expr) => Some(expr), + } + } + + /// Returns true if both yields are prefix or both are postfix. + pub const fn same_kind(&self, other: &Self) -> bool { + match (self, other) { + (YieldKind::Prefix(_), YieldKind::Prefix(_)) => true, + (YieldKind::Postfix(_), YieldKind::Postfix(_)) => true, + _ => false, + } + } +} + /// A literal in a meta item. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItemLit { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 4a1636e6aec0e..b159e136245da 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1813,8 +1813,11 @@ pub fn walk_expr(vis: &mut T, Expr { kind, id, span, attrs, token ExprKind::Paren(expr) => { vis.visit_expr(expr); } - ExprKind::Yield(expr) => { - visit_opt(expr, |expr| vis.visit_expr(expr)); + ExprKind::Yield(kind) => { + let expr = kind.expr_mut(); + if let Some(expr) = expr { + vis.visit_expr(expr); + } } ExprKind::Try(expr) => vis.visit_expr(expr), ExprKind::TryBlock(body) => vis.visit_block(body), diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index e43d78f6e7217..989ebe14bf8fc 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -182,11 +182,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Range(_, Some(e), _) | Ret(Some(e)) | Unary(_, e) - | Yield(Some(e)) | Yeet(Some(e)) | Become(e) => { expr = e; } + Yield(kind) => match kind.expr() { + Some(e) => expr = e, + None => break None, + }, Closure(closure) => { expr = &closure.body; } @@ -217,7 +220,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { Break(_, None) | Range(_, None, _) | Ret(None) - | Yield(None) | Array(_) | Call(_, _) | MethodCall(_) @@ -237,7 +239,9 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Yeet(None) | UnsafeBinderCast(..) | Err(_) - | Dummy => break None, + | Dummy => { + break None; + } } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index cfcb0e23cb5e6..ce8d6df75afb2 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1269,8 +1269,8 @@ 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) => { - visit_opt!(visitor, visit_expr, optional_expression); + ExprKind::Yield(kind) => { + visit_opt!(visitor, visit_expr, kind.expr()); } ExprKind::Try(subexpression) => try_visit!(visitor.visit_expr(subexpression)), ExprKind::TryBlock(body) => try_visit!(visitor.visit_block(body)), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5bb6704dde45a..7f3d060bb8a37 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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(kind) => self.lower_expr_yield(e.span, kind.expr().map(|x| &**x)), ExprKind::Err(guar) => hir::ExprKind::Err(*guar), ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast( diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index e3c41f117abb8..7d9dc89bd7567 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -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; @@ -761,7 +761,7 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); self.pclose(); } - ast::ExprKind::Yield(e) => { + ast::ExprKind::Yield(YieldKind::Prefix(e)) => { self.word("yield"); if let Some(expr) = e { @@ -773,6 +773,14 @@ impl<'a> State<'a> { ); } } + ast::ExprKind::Yield(YieldKind::Postfix(e)) => { + 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, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 9b2d562a69ecd..fc9a511d56a55 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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; @@ -1310,6 +1311,15 @@ impl<'a> Parser<'a> { return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); } + // Parse a postfix `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(lo.to(yield_span), ExprKind::Yield(YieldKind::Postfix(self_arg))) + ); + } + 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)]); @@ -1884,7 +1894,7 @@ impl<'a> Parser<'a> { /// Parse `"yield" expr?`. fn parse_expr_yield(&mut self) -> PResult<'a, P> { let lo = self.prev_token.span; - let kind = ExprKind::Yield(self.parse_expr_opt()?); + let kind = ExprKind::Yield(YieldKind::Prefix(self.parse_expr_opt()?)); let span = lo.to(self.prev_token.span); self.psess.gated_spans.gate(sym::yield_expr, span); let expr = self.mk_expr(span, kind); diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 707312a97f3bc..54261079fcad8 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -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), Yield(r)) => eq_expr_opt(l.expr(), r.expr()) && l.same_kind(r), + (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, _)) => { @@ -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| { diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs index fd2ef9cb1db9d..034ecde068a98 100644 --- a/src/tools/rustfmt/src/chains.rs +++ b/src/tools/rustfmt/src/chains.rs @@ -192,6 +192,7 @@ enum ChainItemKind { StructField(symbol::Ident), TupleField(symbol::Ident, bool), Await, + Yield, Comment(String, CommentPosition), } @@ -203,6 +204,7 @@ impl ChainItemKind { | ChainItemKind::StructField(..) | ChainItemKind::TupleField(..) | ChainItemKind::Await + | ChainItemKind::Yield | ChainItemKind::Comment(..) => false, } } @@ -257,6 +259,10 @@ impl ChainItemKind { let span = mk_sp(nested.span.hi(), expr.span.hi()); (ChainItemKind::Await, span) } + ast::ExprKind::Yield(ast::YieldKind::Postfix(ref nested)) => { + let span = mk_sp(nested.span.hi(), expr.span.hi()); + (ChainItemKind::Yield, span) + } _ => { return ( ChainItemKind::Parent { @@ -306,6 +312,7 @@ impl Rewrite for ChainItem { rewrite_ident(context, ident) ), ChainItemKind::Await => ".await".to_owned(), + ChainItemKind::Yield => ".yield".to_owned(), ChainItemKind::Comment(ref comment, _) => { rewrite_comment(comment, false, shape, context.config)? } @@ -508,7 +515,8 @@ impl Chain { }), ast::ExprKind::Field(ref subexpr, _) | ast::ExprKind::Try(ref subexpr) - | ast::ExprKind::Await(ref subexpr, _) => Some(SubExpr { + | ast::ExprKind::Await(ref subexpr, _) + | ast::ExprKind::Yield(ast::YieldKind::Postfix(ref subexpr)) => Some(SubExpr { expr: Self::convert_try(subexpr, context), is_method_call_receiver: false, }), diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index eff2d2e3ff4a3..e866f13efc73e 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -221,7 +221,7 @@ pub(crate) fn format_expr( Ok(format!("break{id_str}")) } } - ast::ExprKind::Yield(ref opt_expr) => { + ast::ExprKind::Yield(ast::YieldKind::Prefix(ref opt_expr)) => { if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, "yield ", &**expr, shape) } else { @@ -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( diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index fe716c186389f..fcd475b1784f5 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -4,7 +4,7 @@ use rustc_ast::ast::{ self, Attribute, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, Visibility, VisibilityKind, }; -use rustc_ast::ptr; +use rustc_ast::{YieldKind, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol}; use unicode_width::UnicodeWidthStr; @@ -485,7 +485,9 @@ 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(YieldKind::Prefix(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(_) => { @@ -515,7 +517,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(..) | ast::ExprKind::Underscore => false, } } diff --git a/src/tools/rustfmt/tests/target/postfix-yield.rs b/src/tools/rustfmt/tests/target/postfix-yield.rs new file mode 100644 index 0000000000000..8ee34ec431226 --- /dev/null +++ b/src/tools/rustfmt/tests/target/postfix-yield.rs @@ -0,0 +1,17 @@ +// 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).await; + } + ); +} diff --git a/tests/pretty/postfix-yield.rs b/tests/pretty/postfix-yield.rs new file mode 100644 index 0000000000000..f76e8142ae86c --- /dev/null +++ b/tests/pretty/postfix-yield.rs @@ -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; }); +} diff --git a/tests/ui/coroutine/postfix-yield.rs b/tests/ui/coroutine/postfix-yield.rs new file mode 100644 index 0000000000000..ff843138c8c2c --- /dev/null +++ b/tests/ui/coroutine/postfix-yield.rs @@ -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(())); +}