diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cdcd221e811a7..1a18d1964c978 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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>), + /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever), + /// with an optional value to be returned. + Yeet(Option>), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err, } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index d7b1bc6a7f580..4bf3d483f7358 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1394,6 +1394,9 @@ pub fn noop_visit_expr( 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) => { diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 742a7d1d2df70..74b7fe9e24955 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -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 diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e08ba73e0ae31..fa26716083f86 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 37ae41fabf987..5c3e3be21167a 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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 ` 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. // ========================================================================= diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 125acdcc27d97..5a95e5b084ad4 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -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()), }; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 649af48e48adf..0e8af549692fc 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -783,6 +783,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(inline_const, "inline-const is experimental"); gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); gate_all!(associated_const_equality, "associated const equality is incomplete"); + gate_all!(yeet_expr, "`do yeet` expression is experimental"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9de4cbbee13f0..9f44f1b6cc205 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -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); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index f3d4c8ab43843..9159d60463c4c 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -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. // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9318ebb40b09b..b3c22d4ec213d 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -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; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 13e9a5e660fe6..6114e7aaa7bd7 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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> { + 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)) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3889639b50f45..447b73fa3c3ce 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -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`", diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e3ce8105a8b47..c1299c94c4bb3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -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, } diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 9d1e7e81b0e7e..31c1a1d099dc6 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -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; diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index ba369e7f3aaa0..3eaee958b69bd 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -330,6 +330,22 @@ pub trait FromResidual::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(yeeted: Y) -> T +where + T: FromResidual>, +{ + 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 FromResidual for NeverShortCircuit { impl Residual for NeverShortCircuitResidual { type TryType = NeverShortCircuit; } + +/// Implement `FromResidual>` 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(pub T); diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 91e4708f6a609..f339b076dd7d0 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2287,6 +2287,14 @@ impl const ops::FromResidual for Option { } } +#[unstable(feature = "try_trait_v2_yeet", issue = "96374")] +impl ops::FromResidual> for Option { + #[inline] + fn from_residual(ops::Yeet(()): ops::Yeet<()>) -> Self { + None + } +} + #[unstable(feature = "try_trait_v2_residual", issue = "91285")] impl ops::Residual for Option { type TryType = Option; diff --git a/library/core/src/result.rs b/library/core/src/result.rs index b2b132300a299..5e5f8a5ab9543 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -2107,6 +2107,14 @@ impl> const ops::FromResidual> ops::FromResidual> for Result { + #[inline] + fn from_residual(ops::Yeet(e): ops::Yeet) -> Self { + Err(From::from(e)) + } +} + #[unstable(feature = "try_trait_v2_residual", issue = "91285")] impl ops::Residual for Result { type TryType = Result; diff --git a/src/doc/unstable-book/src/language-features/yeet-expr.md b/src/doc/unstable-book/src/language-features/yeet-expr.md new file mode 100644 index 0000000000000..bc1ba4c916b64 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/yeet-expr.md @@ -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 { + do yeet 4; +} +assert_eq!(foo(), Err(4)); + +fn bar() -> Option { + do yeet; +} +assert_eq!(bar(), None); +``` diff --git a/src/test/pretty/yeet-expr.rs b/src/test/pretty/yeet-expr.rs new file mode 100644 index 0000000000000..c899f11b7240e --- /dev/null +++ b/src/test/pretty/yeet-expr.rs @@ -0,0 +1,12 @@ +// pp-exact +#![feature(yeet_expr)] + +fn yeet_no_expr() -> Option { do yeet } + +fn yeet_no_expr_with_semicolon() -> Option { do yeet; } + +fn yeet_with_expr() -> Result { do yeet 1 + 2 } + +fn yeet_with_expr_with_semicolon() -> Result { do yeet 1 + 2; } + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs new file mode 100644 index 0000000000000..a33bd34508c5f --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs @@ -0,0 +1,19 @@ +// compile-flags: --edition 2021 + +pub fn demo() -> Option { + #[cfg(nope)] + { + do yeet //~ ERROR `do yeet` expression is experimental + } + + Some(1) +} + +#[cfg(nope)] +pub fn alternative() -> Result<(), String> { + do yeet "hello"; //~ ERROR `do yeet` expression is experimental +} + +fn main() { + demo(); +} diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr new file mode 100644 index 0000000000000..f90c379bdafe3 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-yeet_expr-in-cfg.stderr @@ -0,0 +1,21 @@ +error[E0658]: `do yeet` expression is experimental + --> $DIR/feature-gate-yeet_expr-in-cfg.rs:6:9 + | +LL | do yeet + | ^^^^^^^ + | + = note: see issue #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-in-cfg.rs:14:5 + | +LL | do yeet "hello"; + | ^^^^^^^^^^^^^^^ + | + = note: see issue #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`. diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr.rs b/src/test/ui/feature-gates/feature-gate-yeet_expr.rs new file mode 100644 index 0000000000000..978a84cf6e5f0 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-yeet_expr.rs @@ -0,0 +1,9 @@ +// compile-flags: --edition 2018 + +pub fn demo() -> Option { + do yeet //~ ERROR `do yeet` expression is experimental +} + +pub fn main() -> Result<(), String> { + do yeet "hello"; //~ ERROR `do yeet` expression is experimental +} diff --git a/src/test/ui/feature-gates/feature-gate-yeet_expr.stderr b/src/test/ui/feature-gates/feature-gate-yeet_expr.stderr new file mode 100644 index 0000000000000..8d1b92370fbe2 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-yeet_expr.stderr @@ -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 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 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`. diff --git a/src/test/ui/inference/question-mark-type-infer.stderr b/src/test/ui/inference/question-mark-type-infer.stderr index e7d5fee18127f..9b822714f828a 100644 --- a/src/test/ui/inference/question-mark-type-infer.stderr +++ b/src/test/ui/inference/question-mark-type-infer.stderr @@ -1,15 +1,9 @@ -error[E0284]: type annotations needed - --> $DIR/question-mark-type-infer.rs:10:21 +error[E0282]: type annotations needed + --> $DIR/question-mark-type-infer.rs:10:30 | LL | l.iter().map(f).collect()? - | ^^^^^^^ cannot infer type - | - = note: cannot satisfy `<_ as Try>::Residual == _` -help: consider specifying the type argument in the method call - | -LL | l.iter().map(f).collect::()? - | +++++ + | ^ cannot infer type error: aborting due to previous error -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/issues/issue-32709.stderr b/src/test/ui/issues/issue-32709.stderr index ed5addcbec517..112cb33593223 100644 --- a/src/test/ui/issues/issue-32709.stderr +++ b/src/test/ui/issues/issue-32709.stderr @@ -7,7 +7,9 @@ LL | Err(5)?; | ^ the trait `From<{integer}>` is not implemented for `()` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait - = help: the trait `FromResidual>` is implemented for `Result` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual>> = note: required because of the requirements on the impl of `FromResidual>` for `Result` error: aborting due to previous error diff --git a/src/test/ui/try-trait/bad-interconversion.stderr b/src/test/ui/try-trait/bad-interconversion.stderr index 1a4105231dc75..1dbf3ebdf827c 100644 --- a/src/test/ui/try-trait/bad-interconversion.stderr +++ b/src/test/ui/try-trait/bad-interconversion.stderr @@ -31,7 +31,9 @@ LL | | } | |_- this function returns a `Result` | = help: the trait `FromResidual>` is not implemented for `Result` - = help: the trait `FromResidual>` is implemented for `Result` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual>> error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result` --> $DIR/bad-interconversion.rs:17:31 @@ -44,7 +46,9 @@ LL | | } | |_- this function returns a `Result` | = help: the trait `FromResidual>` is not implemented for `Result` - = help: the trait `FromResidual>` is implemented for `Result` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual>> error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/bad-interconversion.rs:22:22 @@ -57,7 +61,9 @@ LL | | } | |_- this function returns an `Option` | = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual` is implemented for `Option` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual> error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option` --> $DIR/bad-interconversion.rs:27:33 @@ -70,7 +76,9 @@ LL | | } | |_- this function returns an `Option` | = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual` is implemented for `Option` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual> error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow` --> $DIR/bad-interconversion.rs:32:39 diff --git a/src/test/ui/try-trait/option-to-result.stderr b/src/test/ui/try-trait/option-to-result.stderr index b0e4de8cb4bf5..ae5c3ad628281 100644 --- a/src/test/ui/try-trait/option-to-result.stderr +++ b/src/test/ui/try-trait/option-to-result.stderr @@ -10,7 +10,9 @@ LL | | } | |_- this function returns a `Result` | = help: the trait `FromResidual>` is not implemented for `Result<(), ()>` - = help: the trait `FromResidual>` is implemented for `Result` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual>> error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/option-to-result.rs:11:6 @@ -24,7 +26,9 @@ LL | | } | |_- this function returns an `Option` | = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual` is implemented for `Option` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual> error: aborting due to 2 previous errors diff --git a/src/test/ui/try-trait/try-on-option.stderr b/src/test/ui/try-trait/try-on-option.stderr index 7b2a9a16f900b..ba85a7cada232 100644 --- a/src/test/ui/try-trait/try-on-option.stderr +++ b/src/test/ui/try-trait/try-on-option.stderr @@ -10,7 +10,9 @@ LL | | } | |_- this function returns a `Result` | = help: the trait `FromResidual>` is not implemented for `Result` - = help: the trait `FromResidual>` is implemented for `Result` + = help: the following other types implement trait `FromResidual`: + as FromResidual>> + as FromResidual>> error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option.rs:11:6 diff --git a/src/test/ui/try-trait/yeet-for-option.rs b/src/test/ui/try-trait/yeet-for-option.rs new file mode 100644 index 0000000000000..753fbc1dee7d7 --- /dev/null +++ b/src/test/ui/try-trait/yeet-for-option.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(yeet_expr)] + +fn always_yeet() -> Option { + do yeet; +} + +fn main() { + assert_eq!(always_yeet(), None); +} diff --git a/src/test/ui/try-trait/yeet-for-result.rs b/src/test/ui/try-trait/yeet-for-result.rs new file mode 100644 index 0000000000000..b7b113797cde4 --- /dev/null +++ b/src/test/ui/try-trait/yeet-for-result.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(yeet_expr)] + +fn always_yeet() -> Result { + do yeet "hello"; +} + +fn main() { + assert_eq!(always_yeet(), Err("hello".to_string())); +} diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 1fc9979f3dd7d..794d2e1026f8c 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -214,6 +214,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Path(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) + | ast::ExprKind::Yeet(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Try(..) | ast::ExprKind::TryBlock(..) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 741f3350801db..e4cc93026f10b 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -225,6 +225,10 @@ pub(crate) fn format_expr( ast::ExprKind::Ret(Some(ref expr)) => { rewrite_unary_prefix(context, "return ", &**expr, shape) } + ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), + ast::ExprKind::Yeet(Some(ref expr)) => { + rewrite_unary_prefix(context, "do yeet ", &**expr, shape) + } ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape), ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => { rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape) diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 35512e78fa6e2..ed418fb1fece6 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -512,6 +512,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Range(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) + | ast::ExprKind::Yeet(..) | ast::ExprKind::Tup(..) | ast::ExprKind::Type(..) | ast::ExprKind::Yield(None)