Skip to content

Commit e094ee5

Browse files
committed
Add do yeet expressions to allow experimentation in nightly
Using an obviously-placeholder syntax. An RFC would still be needed before this could have any chance at stabilization, and it might be removed at any point. But I'd really like to have it in nightly at least to ensure it works well with try_trait_v2, especially as we refactor the traits.
1 parent 2c858a7 commit e094ee5

File tree

23 files changed

+236
-3
lines changed

23 files changed

+236
-3
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,7 @@ impl Expr {
12751275
ExprKind::Paren(..) => ExprPrecedence::Paren,
12761276
ExprKind::Try(..) => ExprPrecedence::Try,
12771277
ExprKind::Yield(..) => ExprPrecedence::Yield,
1278+
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
12781279
ExprKind::Err => ExprPrecedence::Err,
12791280
}
12801281
}
@@ -1462,6 +1463,10 @@ pub enum ExprKind {
14621463
/// A `yield`, with an optional value to be yielded.
14631464
Yield(Option<P<Expr>>),
14641465

1466+
/// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
1467+
/// with an optional value to be returned.
1468+
Yeet(Option<P<Expr>>),
1469+
14651470
/// Placeholder for an expression that wasn't syntactically well formed in some way.
14661471
Err,
14671472
}

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,9 @@ pub fn noop_visit_expr<T: MutVisitor>(
13941394
ExprKind::Ret(expr) => {
13951395
visit_opt(expr, |expr| vis.visit_expr(expr));
13961396
}
1397+
ExprKind::Yeet(expr) => {
1398+
visit_opt(expr, |expr| vis.visit_expr(expr));
1399+
}
13971400
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
13981401
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
13991402
ExprKind::Struct(se) => {

compiler/rustc_ast/src/util/parser.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ pub enum ExprPrecedence {
247247
Continue,
248248
Ret,
249249
Yield,
250+
Yeet,
250251

251252
Range,
252253

@@ -299,7 +300,8 @@ impl ExprPrecedence {
299300
ExprPrecedence::Break |
300301
ExprPrecedence::Continue |
301302
ExprPrecedence::Ret |
302-
ExprPrecedence::Yield => PREC_JUMP,
303+
ExprPrecedence::Yield |
304+
ExprPrecedence::Yeet => PREC_JUMP,
303305

304306
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
305307
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence

compiler/rustc_ast/src/visit.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
893893
ExprKind::Ret(ref optional_expression) => {
894894
walk_list!(visitor, visit_expr, optional_expression);
895895
}
896+
ExprKind::Yeet(ref optional_expression) => {
897+
walk_list!(visitor, visit_expr, optional_expression);
898+
}
896899
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
897900
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
898901
ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
221221
let e = e.as_ref().map(|x| self.lower_expr(x));
222222
hir::ExprKind::Ret(e)
223223
}
224+
ExprKind::Yeet(ref sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
224225
ExprKind::InlineAsm(ref asm) => {
225226
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
226227
}
@@ -1543,6 +1544,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
15431544
)
15441545
}
15451546

1547+
/// Desugar `ExprKind::Yeet` from: `do yeet <expr>` into:
1548+
/// ```rust
1549+
/// // If there is an enclosing `try {...}`:
1550+
/// break 'catch_target FromResidual::from_residual(Yeet(residual)),
1551+
/// // Otherwise:
1552+
/// return FromResidual::from_residual(Yeet(residual)),
1553+
/// ```
1554+
/// But to simplify this, there's a `from_yeet` lang item function which
1555+
/// handles the combined `FromResidual::from_residual(Yeet(residual))`.
1556+
fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
1557+
// The expression (if present) or `()` otherwise.
1558+
let (yeeted_span, yeeted_expr) = if let Some(sub_expr) = sub_expr {
1559+
(sub_expr.span, self.lower_expr(sub_expr))
1560+
} else {
1561+
(self.mark_span_with_reason(DesugaringKind::YeetExpr, span, None), self.expr_unit(span))
1562+
};
1563+
1564+
let unstable_span = self.mark_span_with_reason(
1565+
DesugaringKind::YeetExpr,
1566+
span,
1567+
self.allow_try_trait.clone(),
1568+
);
1569+
1570+
let from_yeet_expr = self.wrap_in_try_constructor(
1571+
hir::LangItem::TryTraitFromYeet,
1572+
unstable_span,
1573+
yeeted_expr,
1574+
yeeted_span,
1575+
);
1576+
1577+
if let Some(catch_node) = self.catch_scope {
1578+
let target_id = Ok(self.lower_node_id(catch_node));
1579+
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
1580+
} else {
1581+
hir::ExprKind::Ret(Some(from_yeet_expr))
1582+
}
1583+
}
1584+
15461585
// =========================================================================
15471586
// Helper methods for building HIR.
15481587
// =========================================================================

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
8585
task_context: None,
8686
current_item: None,
8787
captured_lifetimes: None,
88-
allow_try_trait: Some([sym::try_trait_v2][..].into()),
88+
allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
8989
allow_gen_future: Some([sym::gen_future][..].into()),
9090
allow_into_future: Some([sym::into_future][..].into()),
9191
};

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
619619
ast::ExprKind::TryBlock(_) => {
620620
gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
621621
}
622+
ast::ExprKind::Yeet(_) => {
623+
gate_feature_post!(
624+
&self,
625+
yeet_expr,
626+
e.span,
627+
"`do yeet` expression is experimental"
628+
);
629+
}
622630
ast::ExprKind::Block(_, Some(label)) => {
623631
gate_feature_post!(
624632
&self,

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ impl<'a> State<'a> {
6464
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
6565
pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
6666
match expr.kind {
67-
ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true,
67+
ast::ExprKind::Break(..)
68+
| ast::ExprKind::Closure(..)
69+
| ast::ExprKind::Ret(..)
70+
| ast::ExprKind::Yeet(..) => true,
6871
_ => parser::contains_exterior_struct_lit(expr),
6972
}
7073
}
@@ -502,6 +505,15 @@ impl<'a> State<'a> {
502505
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
503506
}
504507
}
508+
ast::ExprKind::Yeet(ref result) => {
509+
self.word("do");
510+
self.word(" ");
511+
self.word("yeet");
512+
if let Some(ref expr) = *result {
513+
self.word(" ");
514+
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
515+
}
516+
}
505517
ast::ExprKind::InlineAsm(ref a) => {
506518
self.word("asm!");
507519
self.print_inline_asm(a);

compiler/rustc_feature/src/active.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,8 @@ declare_features! (
544544
(active, used_with_arg, "1.60.0", Some(93798), None),
545545
/// Allows `extern "wasm" fn`
546546
(active, wasm_abi, "1.53.0", Some(83788), None),
547+
/// Allows `do yeet` expressions
548+
(active, yeet_expr, "1.62.0", Some(96373), None),
547549
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
548550
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
549551
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ language_item_table! {
293293
TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
294294
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
295295
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
296+
TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
296297

297298
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
298299
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,8 @@ impl<'a> Parser<'a> {
13741374
self.parse_break_expr(attrs)
13751375
} else if self.eat_keyword(kw::Yield) {
13761376
self.parse_yield_expr(attrs)
1377+
} else if self.is_do_yeet() {
1378+
self.parse_yeet_expr(attrs)
13771379
} else if self.eat_keyword(kw::Let) {
13781380
self.parse_let_expr(attrs)
13791381
} else if self.eat_keyword(kw::Underscore) {
@@ -1605,6 +1607,21 @@ impl<'a> Parser<'a> {
16051607
self.maybe_recover_from_bad_qpath(expr, true)
16061608
}
16071609

1610+
/// Parse `"do" "yeet" expr?`.
1611+
fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
1612+
let lo = self.token.span;
1613+
1614+
self.bump(); // `do`
1615+
self.bump(); // `yeet`
1616+
1617+
let kind = ExprKind::Yeet(self.parse_expr_opt()?);
1618+
1619+
let span = lo.to(self.prev_token.span);
1620+
self.sess.gated_spans.gate(sym::yeet_expr, span);
1621+
let expr = self.mk_expr(span, kind, attrs);
1622+
self.maybe_recover_from_bad_qpath(expr, true)
1623+
}
1624+
16081625
/// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
16091626
/// If the label is followed immediately by a `:` token, the label and `:` are
16101627
/// parsed as part of the expression (i.e. a labeled loop). The language team has
@@ -2676,6 +2693,10 @@ impl<'a> Parser<'a> {
26762693
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
26772694
}
26782695

2696+
fn is_do_yeet(&self) -> bool {
2697+
self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Yeet])
2698+
}
2699+
26792700
fn is_try_block(&self) -> bool {
26802701
self.token.is_keyword(kw::Try)
26812702
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))

compiler/rustc_span/src/hygiene.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,7 @@ pub enum DesugaringKind {
11321132
CondTemporary,
11331133
QuestionMark,
11341134
TryBlock,
1135+
YeetExpr,
11351136
/// Desugaring of an `impl Trait` in return type position
11361137
/// to an `type Foo = impl Trait;` and replacing the
11371138
/// `impl Trait` with `Foo`.
@@ -1152,6 +1153,7 @@ impl DesugaringKind {
11521153
DesugaringKind::Await => "`await` expression",
11531154
DesugaringKind::QuestionMark => "operator `?`",
11541155
DesugaringKind::TryBlock => "`try` block",
1156+
DesugaringKind::YeetExpr => "`do yeet` expression",
11551157
DesugaringKind::OpaqueTy => "`impl Trait`",
11561158
DesugaringKind::ForLoop => "`for` loop",
11571159
DesugaringKind::LetElse => "`let...else`",

compiler/rustc_span/src/symbol.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ symbols! {
101101
MacroRules: "macro_rules",
102102
Raw: "raw",
103103
Union: "union",
104+
Yeet: "yeet",
104105
}
105106

106107
// Pre-interned symbols that can be referred to with `rustc_span::sym::*`.
@@ -714,6 +715,7 @@ symbols! {
714715
from_residual,
715716
from_size_align_unchecked,
716717
from_usize,
718+
from_yeet,
717719
fsub_fast,
718720
fundamental,
719721
future,
@@ -1534,6 +1536,8 @@ symbols! {
15341536
x87_reg,
15351537
xer,
15361538
xmm_reg,
1539+
yeet_desugar_details,
1540+
yeet_expr,
15371541
ymm_reg,
15381542
zmm_reg,
15391543
}

library/core/src/ops/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ pub use self::range::OneSidedRange;
187187
#[unstable(feature = "try_trait_v2", issue = "84277")]
188188
pub use self::try_trait::{FromResidual, Try};
189189

190+
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
191+
pub use self::try_trait::Yeet;
192+
190193
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
191194
pub use self::try_trait::Residual;
192195

library/core/src/ops/try_trait.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,22 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
330330
fn from_residual(residual: R) -> Self;
331331
}
332332

333+
#[cfg(not(bootstrap))]
334+
#[unstable(
335+
feature = "yeet_desugar_details",
336+
issue = "none",
337+
reason = "just here to simplify the desugaring; will never be stabilized"
338+
)]
339+
#[inline]
340+
#[track_caller] // because `Result::from_residual` has it
341+
#[lang = "from_yeet"]
342+
pub fn from_yeet<T, Y>(yeeted: Y) -> T
343+
where
344+
T: FromResidual<Yeet<Y>>,
345+
{
346+
FromResidual::from_residual(Yeet(yeeted))
347+
}
348+
333349
/// Allows retrieving the canonical type implementing [`Try`] that has this type
334350
/// as its residual and allows it to hold an `O` as its output.
335351
///
@@ -395,3 +411,9 @@ impl<T> FromResidual for NeverShortCircuit<T> {
395411
impl<T> Residual<T> for NeverShortCircuitResidual {
396412
type TryType = NeverShortCircuit<T>;
397413
}
414+
415+
/// Implement `FromResidual<Yeet<T>>` on your type to enable
416+
/// `do yeet expr` syntax in functions returning your type.
417+
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
418+
#[derive(Debug)]
419+
pub struct Yeet<T>(pub T);

library/core/src/option.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2287,6 +2287,14 @@ impl<T> const ops::FromResidual for Option<T> {
22872287
}
22882288
}
22892289

2290+
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
2291+
impl<T> ops::FromResidual<ops::Yeet<()>> for Option<T> {
2292+
#[inline]
2293+
fn from_residual(ops::Yeet(()): ops::Yeet<()>) -> Self {
2294+
None
2295+
}
2296+
}
2297+
22902298
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
22912299
impl<T> ops::Residual<T> for Option<convert::Infallible> {
22922300
type TryType = Option<T>;

library/core/src/result.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,14 @@ impl<T, E, F: ~const From<E>> const ops::FromResidual<Result<convert::Infallible
21072107
}
21082108
}
21092109

2110+
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
2111+
impl<T, E, F: From<E>> ops::FromResidual<ops::Yeet<E>> for Result<T, F> {
2112+
#[inline]
2113+
fn from_residual(ops::Yeet(e): ops::Yeet<E>) -> Self {
2114+
Err(From::from(e))
2115+
}
2116+
}
2117+
21102118
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
21112119
impl<T, E> ops::Residual<T> for Result<convert::Infallible, E> {
21122120
type TryType = Result<T, E>;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# `yeet_expr`
2+
3+
The tracking issue for this feature is: [#96373]
4+
5+
[#96373]: https://github.com/rust-lang/rust/issues/96373
6+
7+
------------------------
8+
9+
The `yeet_expr` feature adds support for `do yeet` expressions,
10+
which can be used to early-exit from a function or `try` block.
11+
12+
These are highly experimental, thus the placeholder syntax.
13+
14+
```rust,edition2021
15+
#![feature(yeet_expr)]
16+
17+
fn foo() -> Result<String, i32> {
18+
do yeet 4;
19+
}
20+
assert_eq!(foo(), Err(4));
21+
22+
fn bar() -> Option<String> {
23+
do yeet;
24+
}
25+
assert_eq!(bar(), None);
26+
```

src/test/pretty/yeet-expr.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// pp-exact
2+
#![feature(yeet_expr)]
3+
4+
fn yeet_no_expr() -> Option<String> { do yeet }
5+
6+
fn yeet_no_expr_with_semicolon() -> Option<String> { do yeet; }
7+
8+
fn yeet_with_expr() -> Result<String, i32> { do yeet 1 + 2 }
9+
10+
fn yeet_with_expr_with_semicolon() -> Result<String, i32> { do yeet 1 + 2; }
11+
12+
fn main() {}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// compile-flags: --edition 2018
2+
3+
pub fn demo() -> Option<i32> {
4+
do yeet //~ ERROR `do yeet` expression is experimental
5+
}
6+
7+
pub fn main() -> Result<(), String> {
8+
do yeet "hello"; //~ ERROR `do yeet` expression is experimental
9+
}

0 commit comments

Comments
 (0)