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.
This commit is contained in:
parent
2c858a7c3f
commit
e094ee5f10
compiler
rustc_ast/src
rustc_ast_lowering/src
rustc_ast_passes/src
rustc_ast_pretty/src/pprust/state
rustc_feature/src
rustc_hir/src
rustc_parse/src/parser
rustc_span/src
library/core/src
src
doc/unstable-book/src/language-features
test
pretty
ui
@ -1275,6 +1275,7 @@ impl Expr {
|
||||
ExprKind::Paren(..) => ExprPrecedence::Paren,
|
||||
ExprKind::Try(..) => ExprPrecedence::Try,
|
||||
ExprKind::Yield(..) => ExprPrecedence::Yield,
|
||||
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
|
||||
ExprKind::Err => ExprPrecedence::Err,
|
||||
}
|
||||
}
|
||||
@ -1462,6 +1463,10 @@ pub enum ExprKind {
|
||||
/// A `yield`, with an optional value to be yielded.
|
||||
Yield(Option<P<Expr>>),
|
||||
|
||||
/// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
|
||||
/// with an optional value to be returned.
|
||||
Yeet(Option<P<Expr>>),
|
||||
|
||||
/// Placeholder for an expression that wasn't syntactically well formed in some way.
|
||||
Err,
|
||||
}
|
||||
|
@ -1394,6 +1394,9 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
ExprKind::Ret(expr) => {
|
||||
visit_opt(expr, |expr| vis.visit_expr(expr));
|
||||
}
|
||||
ExprKind::Yeet(expr) => {
|
||||
visit_opt(expr, |expr| vis.visit_expr(expr));
|
||||
}
|
||||
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
|
||||
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||
ExprKind::Struct(se) => {
|
||||
|
@ -247,6 +247,7 @@ pub enum ExprPrecedence {
|
||||
Continue,
|
||||
Ret,
|
||||
Yield,
|
||||
Yeet,
|
||||
|
||||
Range,
|
||||
|
||||
@ -299,7 +300,8 @@ impl ExprPrecedence {
|
||||
ExprPrecedence::Break |
|
||||
ExprPrecedence::Continue |
|
||||
ExprPrecedence::Ret |
|
||||
ExprPrecedence::Yield => PREC_JUMP,
|
||||
ExprPrecedence::Yield |
|
||||
ExprPrecedence::Yeet => PREC_JUMP,
|
||||
|
||||
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
|
||||
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
|
||||
|
@ -893,6 +893,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||
ExprKind::Ret(ref optional_expression) => {
|
||||
walk_list!(visitor, visit_expr, optional_expression);
|
||||
}
|
||||
ExprKind::Yeet(ref optional_expression) => {
|
||||
walk_list!(visitor, visit_expr, optional_expression);
|
||||
}
|
||||
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
|
||||
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
|
||||
ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
|
||||
|
@ -221,6 +221,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let e = e.as_ref().map(|x| self.lower_expr(x));
|
||||
hir::ExprKind::Ret(e)
|
||||
}
|
||||
ExprKind::Yeet(ref sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
|
||||
ExprKind::InlineAsm(ref asm) => {
|
||||
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
|
||||
}
|
||||
@ -1543,6 +1544,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Desugar `ExprKind::Yeet` from: `do yeet <expr>` into:
|
||||
/// ```rust
|
||||
/// // If there is an enclosing `try {...}`:
|
||||
/// break 'catch_target FromResidual::from_residual(Yeet(residual)),
|
||||
/// // Otherwise:
|
||||
/// return FromResidual::from_residual(Yeet(residual)),
|
||||
/// ```
|
||||
/// But to simplify this, there's a `from_yeet` lang item function which
|
||||
/// handles the combined `FromResidual::from_residual(Yeet(residual))`.
|
||||
fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
|
||||
// The expression (if present) or `()` otherwise.
|
||||
let (yeeted_span, yeeted_expr) = if let Some(sub_expr) = sub_expr {
|
||||
(sub_expr.span, self.lower_expr(sub_expr))
|
||||
} else {
|
||||
(self.mark_span_with_reason(DesugaringKind::YeetExpr, span, None), self.expr_unit(span))
|
||||
};
|
||||
|
||||
let unstable_span = self.mark_span_with_reason(
|
||||
DesugaringKind::YeetExpr,
|
||||
span,
|
||||
self.allow_try_trait.clone(),
|
||||
);
|
||||
|
||||
let from_yeet_expr = self.wrap_in_try_constructor(
|
||||
hir::LangItem::TryTraitFromYeet,
|
||||
unstable_span,
|
||||
yeeted_expr,
|
||||
yeeted_span,
|
||||
);
|
||||
|
||||
if let Some(catch_node) = self.catch_scope {
|
||||
let target_id = Ok(self.lower_node_id(catch_node));
|
||||
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
|
||||
} else {
|
||||
hir::ExprKind::Ret(Some(from_yeet_expr))
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Helper methods for building HIR.
|
||||
// =========================================================================
|
||||
|
@ -85,7 +85,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
|
||||
task_context: None,
|
||||
current_item: None,
|
||||
captured_lifetimes: None,
|
||||
allow_try_trait: Some([sym::try_trait_v2][..].into()),
|
||||
allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
|
||||
allow_gen_future: Some([sym::gen_future][..].into()),
|
||||
allow_into_future: Some([sym::into_future][..].into()),
|
||||
};
|
||||
|
@ -619,6 +619,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
ast::ExprKind::TryBlock(_) => {
|
||||
gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
|
||||
}
|
||||
ast::ExprKind::Yeet(_) => {
|
||||
gate_feature_post!(
|
||||
&self,
|
||||
yeet_expr,
|
||||
e.span,
|
||||
"`do yeet` expression is experimental"
|
||||
);
|
||||
}
|
||||
ast::ExprKind::Block(_, Some(label)) => {
|
||||
gate_feature_post!(
|
||||
&self,
|
||||
|
@ -64,7 +64,10 @@ impl<'a> State<'a> {
|
||||
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
|
||||
pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
|
||||
match expr.kind {
|
||||
ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true,
|
||||
ast::ExprKind::Break(..)
|
||||
| ast::ExprKind::Closure(..)
|
||||
| ast::ExprKind::Ret(..)
|
||||
| ast::ExprKind::Yeet(..) => true,
|
||||
_ => parser::contains_exterior_struct_lit(expr),
|
||||
}
|
||||
}
|
||||
@ -502,6 +505,15 @@ impl<'a> State<'a> {
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Yeet(ref result) => {
|
||||
self.word("do");
|
||||
self.word(" ");
|
||||
self.word("yeet");
|
||||
if let Some(ref expr) = *result {
|
||||
self.word(" ");
|
||||
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
|
||||
}
|
||||
}
|
||||
ast::ExprKind::InlineAsm(ref a) => {
|
||||
self.word("asm!");
|
||||
self.print_inline_asm(a);
|
||||
|
@ -544,6 +544,8 @@ declare_features! (
|
||||
(active, used_with_arg, "1.60.0", Some(93798), None),
|
||||
/// Allows `extern "wasm" fn`
|
||||
(active, wasm_abi, "1.53.0", Some(83788), None),
|
||||
/// Allows `do yeet` expressions
|
||||
(active, yeet_expr, "1.62.0", Some(96373), None),
|
||||
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
|
||||
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
|
||||
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
|
||||
|
@ -293,6 +293,7 @@ language_item_table! {
|
||||
TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
|
||||
TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
|
||||
|
||||
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
|
||||
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
|
||||
|
@ -1374,6 +1374,8 @@ impl<'a> Parser<'a> {
|
||||
self.parse_break_expr(attrs)
|
||||
} else if self.eat_keyword(kw::Yield) {
|
||||
self.parse_yield_expr(attrs)
|
||||
} else if self.is_do_yeet() {
|
||||
self.parse_yeet_expr(attrs)
|
||||
} else if self.eat_keyword(kw::Let) {
|
||||
self.parse_let_expr(attrs)
|
||||
} else if self.eat_keyword(kw::Underscore) {
|
||||
@ -1605,6 +1607,21 @@ impl<'a> Parser<'a> {
|
||||
self.maybe_recover_from_bad_qpath(expr, true)
|
||||
}
|
||||
|
||||
/// Parse `"do" "yeet" expr?`.
|
||||
fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
|
||||
let lo = self.token.span;
|
||||
|
||||
self.bump(); // `do`
|
||||
self.bump(); // `yeet`
|
||||
|
||||
let kind = ExprKind::Yeet(self.parse_expr_opt()?);
|
||||
|
||||
let span = lo.to(self.prev_token.span);
|
||||
self.sess.gated_spans.gate(sym::yeet_expr, span);
|
||||
let expr = self.mk_expr(span, kind, attrs);
|
||||
self.maybe_recover_from_bad_qpath(expr, true)
|
||||
}
|
||||
|
||||
/// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
|
||||
/// If the label is followed immediately by a `:` token, the label and `:` are
|
||||
/// parsed as part of the expression (i.e. a labeled loop). The language team has
|
||||
@ -2676,6 +2693,10 @@ impl<'a> Parser<'a> {
|
||||
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
|
||||
}
|
||||
|
||||
fn is_do_yeet(&self) -> bool {
|
||||
self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Yeet])
|
||||
}
|
||||
|
||||
fn is_try_block(&self) -> bool {
|
||||
self.token.is_keyword(kw::Try)
|
||||
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
|
||||
|
@ -1132,6 +1132,7 @@ pub enum DesugaringKind {
|
||||
CondTemporary,
|
||||
QuestionMark,
|
||||
TryBlock,
|
||||
YeetExpr,
|
||||
/// Desugaring of an `impl Trait` in return type position
|
||||
/// to an `type Foo = impl Trait;` and replacing the
|
||||
/// `impl Trait` with `Foo`.
|
||||
@ -1152,6 +1153,7 @@ impl DesugaringKind {
|
||||
DesugaringKind::Await => "`await` expression",
|
||||
DesugaringKind::QuestionMark => "operator `?`",
|
||||
DesugaringKind::TryBlock => "`try` block",
|
||||
DesugaringKind::YeetExpr => "`do yeet` expression",
|
||||
DesugaringKind::OpaqueTy => "`impl Trait`",
|
||||
DesugaringKind::ForLoop => "`for` loop",
|
||||
DesugaringKind::LetElse => "`let...else`",
|
||||
|
@ -101,6 +101,7 @@ symbols! {
|
||||
MacroRules: "macro_rules",
|
||||
Raw: "raw",
|
||||
Union: "union",
|
||||
Yeet: "yeet",
|
||||
}
|
||||
|
||||
// Pre-interned symbols that can be referred to with `rustc_span::sym::*`.
|
||||
@ -714,6 +715,7 @@ symbols! {
|
||||
from_residual,
|
||||
from_size_align_unchecked,
|
||||
from_usize,
|
||||
from_yeet,
|
||||
fsub_fast,
|
||||
fundamental,
|
||||
future,
|
||||
@ -1534,6 +1536,8 @@ symbols! {
|
||||
x87_reg,
|
||||
xer,
|
||||
xmm_reg,
|
||||
yeet_desugar_details,
|
||||
yeet_expr,
|
||||
ymm_reg,
|
||||
zmm_reg,
|
||||
}
|
||||
|
@ -187,6 +187,9 @@ pub use self::range::OneSidedRange;
|
||||
#[unstable(feature = "try_trait_v2", issue = "84277")]
|
||||
pub use self::try_trait::{FromResidual, Try};
|
||||
|
||||
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
|
||||
pub use self::try_trait::Yeet;
|
||||
|
||||
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
|
||||
pub use self::try_trait::Residual;
|
||||
|
||||
|
@ -330,6 +330,22 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||
fn from_residual(residual: R) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(
|
||||
feature = "yeet_desugar_details",
|
||||
issue = "none",
|
||||
reason = "just here to simplify the desugaring; will never be stabilized"
|
||||
)]
|
||||
#[inline]
|
||||
#[track_caller] // because `Result::from_residual` has it
|
||||
#[lang = "from_yeet"]
|
||||
pub fn from_yeet<T, Y>(yeeted: Y) -> T
|
||||
where
|
||||
T: FromResidual<Yeet<Y>>,
|
||||
{
|
||||
FromResidual::from_residual(Yeet(yeeted))
|
||||
}
|
||||
|
||||
/// Allows retrieving the canonical type implementing [`Try`] that has this type
|
||||
/// as its residual and allows it to hold an `O` as its output.
|
||||
///
|
||||
@ -395,3 +411,9 @@ impl<T> FromResidual for NeverShortCircuit<T> {
|
||||
impl<T> Residual<T> for NeverShortCircuitResidual {
|
||||
type TryType = NeverShortCircuit<T>;
|
||||
}
|
||||
|
||||
/// Implement `FromResidual<Yeet<T>>` on your type to enable
|
||||
/// `do yeet expr` syntax in functions returning your type.
|
||||
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
|
||||
#[derive(Debug)]
|
||||
pub struct Yeet<T>(pub T);
|
||||
|
@ -2287,6 +2287,14 @@ impl<T> const ops::FromResidual for Option<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
|
||||
impl<T> ops::FromResidual<ops::Yeet<()>> for Option<T> {
|
||||
#[inline]
|
||||
fn from_residual(ops::Yeet(()): ops::Yeet<()>) -> Self {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
|
||||
impl<T> ops::Residual<T> for Option<convert::Infallible> {
|
||||
type TryType = Option<T>;
|
||||
|
@ -2107,6 +2107,14 @@ impl<T, E, F: ~const From<E>> const ops::FromResidual<Result<convert::Infallible
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
|
||||
impl<T, E, F: From<E>> ops::FromResidual<ops::Yeet<E>> for Result<T, F> {
|
||||
#[inline]
|
||||
fn from_residual(ops::Yeet(e): ops::Yeet<E>) -> Self {
|
||||
Err(From::from(e))
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
|
||||
impl<T, E> ops::Residual<T> for Result<convert::Infallible, E> {
|
||||
type TryType = Result<T, E>;
|
||||
|
26
src/doc/unstable-book/src/language-features/yeet-expr.md
Normal file
26
src/doc/unstable-book/src/language-features/yeet-expr.md
Normal file
@ -0,0 +1,26 @@
|
||||
# `yeet_expr`
|
||||
|
||||
The tracking issue for this feature is: [#96373]
|
||||
|
||||
[#96373]: https://github.com/rust-lang/rust/issues/96373
|
||||
|
||||
------------------------
|
||||
|
||||
The `yeet_expr` feature adds support for `do yeet` expressions,
|
||||
which can be used to early-exit from a function or `try` block.
|
||||
|
||||
These are highly experimental, thus the placeholder syntax.
|
||||
|
||||
```rust,edition2021
|
||||
#![feature(yeet_expr)]
|
||||
|
||||
fn foo() -> Result<String, i32> {
|
||||
do yeet 4;
|
||||
}
|
||||
assert_eq!(foo(), Err(4));
|
||||
|
||||
fn bar() -> Option<String> {
|
||||
do yeet;
|
||||
}
|
||||
assert_eq!(bar(), None);
|
||||
```
|
12
src/test/pretty/yeet-expr.rs
Normal file
12
src/test/pretty/yeet-expr.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// pp-exact
|
||||
#![feature(yeet_expr)]
|
||||
|
||||
fn yeet_no_expr() -> Option<String> { do yeet }
|
||||
|
||||
fn yeet_no_expr_with_semicolon() -> Option<String> { do yeet; }
|
||||
|
||||
fn yeet_with_expr() -> Result<String, i32> { do yeet 1 + 2 }
|
||||
|
||||
fn yeet_with_expr_with_semicolon() -> Result<String, i32> { do yeet 1 + 2; }
|
||||
|
||||
fn main() {}
|
9
src/test/ui/feature-gates/feature-gate-yeet_expr.rs
Normal file
9
src/test/ui/feature-gates/feature-gate-yeet_expr.rs
Normal file
@ -0,0 +1,9 @@
|
||||
// compile-flags: --edition 2018
|
||||
|
||||
pub fn demo() -> Option<i32> {
|
||||
do yeet //~ ERROR `do yeet` expression is experimental
|
||||
}
|
||||
|
||||
pub fn main() -> Result<(), String> {
|
||||
do yeet "hello"; //~ ERROR `do yeet` expression is experimental
|
||||
}
|
21
src/test/ui/feature-gates/feature-gate-yeet_expr.stderr
Normal file
21
src/test/ui/feature-gates/feature-gate-yeet_expr.stderr
Normal file
@ -0,0 +1,21 @@
|
||||
error[E0658]: `do yeet` expression is experimental
|
||||
--> $DIR/feature-gate-yeet_expr.rs:4:5
|
||||
|
|
||||
LL | do yeet
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
|
||||
= help: add `#![feature(yeet_expr)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: `do yeet` expression is experimental
|
||||
--> $DIR/feature-gate-yeet_expr.rs:8:5
|
||||
|
|
||||
LL | do yeet "hello";
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
|
||||
= help: add `#![feature(yeet_expr)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
11
src/test/ui/try-trait/yeet-for-option.rs
Normal file
11
src/test/ui/try-trait/yeet-for-option.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(yeet_expr)]
|
||||
|
||||
fn always_yeet() -> Option<String> {
|
||||
do yeet;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(always_yeet(), None);
|
||||
}
|
11
src/test/ui/try-trait/yeet-for-result.rs
Normal file
11
src/test/ui/try-trait/yeet-for-result.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// run-pass
|
||||
|
||||
#![feature(yeet_expr)]
|
||||
|
||||
fn always_yeet() -> Result<i32, String> {
|
||||
do yeet "hello";
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(always_yeet(), Err("hello".to_string()));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user