about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-06-26 13:51:04 +0000
committerbors <bors@rust-lang.org>2023-06-26 13:51:04 +0000
commit6f8c27ae89dfd32895419d7ef5b89844bcad1bcd (patch)
tree2b24dea873afef325e5fc0bdeca362c4c51ffa1e
parent27e10c5292eee22abef69aed7144f80bdea00603 (diff)
parente38576a893b30d8f2a71665e28498219dcc907e3 (diff)
downloadrust-6f8c27ae89dfd32895419d7ef5b89844bcad1bcd.tar.gz
rust-6f8c27ae89dfd32895419d7ef5b89844bcad1bcd.zip
Auto merge of #112887 - WaffleLapkin:become_unuwuable_in_hir, r=compiler-errors,Nilstrieb
`hir`: Add `Become` expression kind (explicit tail calls experiment)

This adds `hir::ExprKind::Become` alongside ast lowering. During hir-thir lowering we currently lower `become` as `return`, so that we can partially test `become` without ICEing.

cc `@scottmcm`
r? `@Nilstrieb`
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs5
-rw-r--r--compiler/rustc_hir/src/intravisit.rs1
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs5
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl4
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs23
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs132
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs1
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs5
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs4
-rw-r--r--compiler/rustc_passes/src/liveness.rs7
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs1
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--tests/ui/explicit-tail-calls/become-outside.array.stderr9
-rw-r--r--tests/ui/explicit-tail-calls/become-outside.constant.stderr9
-rw-r--r--tests/ui/explicit-tail-calls/become-outside.rs15
-rw-r--r--tests/ui/explicit-tail-calls/return-lifetime-sub.rs13
-rw-r--r--tests/ui/explicit-tail-calls/return-mismatches.rs28
-rw-r--r--tests/ui/explicit-tail-calls/return-mismatches.stderr27
-rw-r--r--tests/ui/issues/issue-51714.rs10
-rw-r--r--tests/ui/issues/issue-51714.stderr6
-rw-r--r--tests/ui/return/issue-64620.rs2
-rw-r--r--tests/ui/return/issue-64620.stderr2
-rw-r--r--tests/ui/return/issue-86188-return-not-in-fn-body.rs12
-rw-r--r--tests/ui/return/tail-expr-as-potential-return.rs4
-rw-r--r--tests/ui/typeck/issue-86721-return-expr-ice.rs2
35 files changed, 286 insertions, 72 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 225714a1361..29972dd76eb 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -277,9 +277,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
                 ExprKind::Become(sub_expr) => {
                     let sub_expr = self.lower_expr(sub_expr);
-
-                    // FIXME(explicit_tail_calls): Use `hir::ExprKind::Become` once we implemented it
-                    hir::ExprKind::Ret(Some(sub_expr))
+                    hir::ExprKind::Become(sub_expr)
                 }
                 ExprKind::InlineAsm(asm) => {
                     hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 5e5001bc8b4..591831b59a1 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1719,6 +1719,7 @@ impl Expr<'_> {
             ExprKind::Break(..) => ExprPrecedence::Break,
             ExprKind::Continue(..) => ExprPrecedence::Continue,
             ExprKind::Ret(..) => ExprPrecedence::Ret,
+            ExprKind::Become(..) => ExprPrecedence::Become,
             ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
             ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf,
             ExprKind::Struct(..) => ExprPrecedence::Struct,
@@ -1776,6 +1777,7 @@ impl Expr<'_> {
             | ExprKind::Break(..)
             | ExprKind::Continue(..)
             | ExprKind::Ret(..)
+            | ExprKind::Become(..)
             | ExprKind::Let(..)
             | ExprKind::Loop(..)
             | ExprKind::Assign(..)
@@ -1866,6 +1868,7 @@ impl Expr<'_> {
             | ExprKind::Break(..)
             | ExprKind::Continue(..)
             | ExprKind::Ret(..)
+            | ExprKind::Become(..)
             | ExprKind::Let(..)
             | ExprKind::Loop(..)
             | ExprKind::Assign(..)
@@ -2025,6 +2028,8 @@ pub enum ExprKind<'hir> {
     Continue(Destination),
     /// A `return`, with an optional value to be returned.
     Ret(Option<&'hir Expr<'hir>>),
+    /// A `become`, with the value to be returned.
+    Become(&'hir Expr<'hir>),
 
     /// Inline assembly (from `asm!`), with its outputs and inputs.
     InlineAsm(&'hir InlineAsm<'hir>),
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index f84c814bd92..1886a91bda8 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -791,6 +791,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
         ExprKind::Ret(ref optional_expression) => {
             walk_list!(visitor, visit_expr, optional_expression);
         }
+        ExprKind::Become(ref expr) => visitor.visit_expr(expr),
         ExprKind::InlineAsm(ref asm) => {
             visitor.visit_inline_asm(asm, expression.hir_id);
         }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index ced46fe426c..a699cd6c942 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1554,6 +1554,11 @@ impl<'a> State<'a> {
                     self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
                 }
             }
+            hir::ExprKind::Become(result) => {
+                self.word("become");
+                self.word(" ");
+                self.print_expr_maybe_paren(result, parser::PREC_JUMP);
+            }
             hir::ExprKind::InlineAsm(asm) => {
                 self.word("asm!");
                 self.print_inline_asm(asm);
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index c1c58db5764..3d012a15a67 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -78,8 +78,8 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang
 hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
 
 hir_typeck_return_stmt_outside_of_fn_body =
-    return statement outside of function body
-    .encl_body_label = the return is part of this body...
+    {$statement_kind} statement outside of function body
+    .encl_body_label = the {$statement_kind} is part of this body...
     .encl_fn_label = ...not the enclosing function body
 
 hir_typeck_struct_expr_non_exhaustive =
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 6b4168d8944..05906a4b9f5 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -2,7 +2,10 @@
 use std::borrow::Cow;
 
 use crate::fluent_generated as fluent;
-use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage};
+use rustc_errors::{
+    AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, MultiSpan,
+    SubdiagnosticMessage,
+};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_span::{
@@ -31,6 +34,24 @@ pub struct ReturnStmtOutsideOfFnBody {
     pub encl_body_span: Option<Span>,
     #[label(hir_typeck_encl_fn_label)]
     pub encl_fn_span: Option<Span>,
+    pub statement_kind: ReturnLikeStatementKind,
+}
+
+pub enum ReturnLikeStatementKind {
+    Return,
+    Become,
+}
+
+impl IntoDiagnosticArg for ReturnLikeStatementKind {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        let kind = match self {
+            Self::Return => "return",
+            Self::Become => "become",
+        }
+        .into();
+
+        DiagnosticArgValue::Str(kind)
+    }
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index e250e68a7f1..8d621c5a42b 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -5,6 +5,7 @@
 use crate::cast;
 use crate::coercion::CoerceMany;
 use crate::coercion::DynamicCoerceMany;
+use crate::errors::ReturnLikeStatementKind;
 use crate::errors::TypeMismatchFruTypo;
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use crate::errors::{
@@ -324,6 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
+            ExprKind::Become(call) => self.check_expr_become(call, expr),
             ExprKind::Let(let_expr) => self.check_expr_let(let_expr),
             ExprKind::Loop(body, _, source, _) => {
                 self.check_expr_loop(body, source, expected, expr)
@@ -735,47 +737,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
         if self.ret_coercion.is_none() {
-            let mut err = ReturnStmtOutsideOfFnBody {
-                span: expr.span,
-                encl_body_span: None,
-                encl_fn_span: None,
-            };
-
-            let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
-
-            if let Some(hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Fn(..),
-                span: encl_fn_span,
-                ..
-            }))
-            | Some(hir::Node::TraitItem(hir::TraitItem {
-                kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
-                span: encl_fn_span,
-                ..
-            }))
-            | Some(hir::Node::ImplItem(hir::ImplItem {
-                kind: hir::ImplItemKind::Fn(..),
-                span: encl_fn_span,
-                ..
-            })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
-            {
-                // We are inside a function body, so reporting "return statement
-                // outside of function body" needs an explanation.
-
-                let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
-
-                // If this didn't hold, we would not have to report an error in
-                // the first place.
-                assert_ne!(encl_item_id.def_id, encl_body_owner_id);
-
-                let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
-                let encl_body = self.tcx.hir().body(encl_body_id);
-
-                err.encl_body_span = Some(encl_body.value.span);
-                err.encl_fn_span = Some(*encl_fn_span);
-            }
-
-            self.tcx.sess.emit_err(err);
+            self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
 
             if let Some(e) = expr_opt {
                 // We still have to type-check `e` (issue #86188), but calling
@@ -815,6 +777,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx.types.never
     }
 
+    fn check_expr_become(
+        &self,
+        call: &'tcx hir::Expr<'tcx>,
+        expr: &'tcx hir::Expr<'tcx>,
+    ) -> Ty<'tcx> {
+        match &self.ret_coercion {
+            Some(ret_coercion) => {
+                let ret_ty = ret_coercion.borrow().expected_ty();
+                let call_expr_ty = self.check_expr_with_hint(call, ret_ty);
+
+                // N.B. don't coerce here, as tail calls can't support most/all coercions
+                // FIXME(explicit_tail_calls): add a diagnostic note that `become` doesn't allow coercions
+                self.demand_suptype(expr.span, ret_ty, call_expr_ty);
+            }
+            None => {
+                self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Become);
+
+                // Fallback to simply type checking `call` without hint/demanding the right types.
+                // Best effort to highlight more errors.
+                self.check_expr(call);
+            }
+        }
+
+        self.tcx.types.never
+    }
+
+    /// Check an expression that _is being returned_.
+    /// For example, this is called with `return_expr: $expr` when `return $expr`
+    /// is encountered.
+    ///
+    /// Note that this function must only be called in function bodies.
+    ///
     /// `explicit_return` is `true` if we're checking an explicit `return expr`,
     /// and `false` if we're checking a trailing expression.
     pub(super) fn check_return_expr(
@@ -831,10 +825,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut span = return_expr.span;
         // Use the span of the trailing expression for our cause,
         // not the span of the entire function
-        if !explicit_return {
-            if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr {
+        if !explicit_return
+            && let ExprKind::Block(body, _) = return_expr.kind
+            && let Some(last_expr) = body.expr
+        {
                 span = last_expr.span;
-            }
         }
         ret_coercion.borrow_mut().coerce(
             self,
@@ -854,6 +849,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Emit an error because `return` or `become` is used outside of a function body.
+    ///
+    /// `expr` is the `return` (`become`) "statement", `kind` is the kind of the statement
+    /// either `Return` or `Become`.
+    fn emit_return_outside_of_fn_body(&self, expr: &hir::Expr<'_>, kind: ReturnLikeStatementKind) {
+        let mut err = ReturnStmtOutsideOfFnBody {
+            span: expr.span,
+            encl_body_span: None,
+            encl_fn_span: None,
+            statement_kind: kind,
+        };
+
+        let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
+
+        if let Some(hir::Node::Item(hir::Item {
+            kind: hir::ItemKind::Fn(..),
+            span: encl_fn_span,
+            ..
+        }))
+        | Some(hir::Node::TraitItem(hir::TraitItem {
+            kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
+            span: encl_fn_span,
+            ..
+        }))
+        | Some(hir::Node::ImplItem(hir::ImplItem {
+            kind: hir::ImplItemKind::Fn(..),
+            span: encl_fn_span,
+            ..
+        })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
+        {
+            // We are inside a function body, so reporting "return statement
+            // outside of function body" needs an explanation.
+
+            let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
+
+            // If this didn't hold, we would not have to report an error in
+            // the first place.
+            assert_ne!(encl_item_id.def_id, encl_body_owner_id);
+
+            let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
+            let encl_body = self.tcx.hir().body(encl_body_id);
+
+            err.encl_body_span = Some(encl_body.value.span);
+            err.encl_fn_span = Some(*encl_fn_span);
+        }
+
+        self.tcx.sess.emit_err(err);
+    }
+
     fn point_at_return_for_opaque_ty_error(
         &self,
         errors: &mut Vec<traits::FulfillmentError<'tcx>>,
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 82d9f03b145..0d2e0602e1c 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -326,6 +326,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                 }
             }
 
+            hir::ExprKind::Become(call) => {
+                self.consume_expr(call);
+            }
+
             hir::ExprKind::Assign(lhs, rhs, _) => {
                 self.mutate_expr(lhs);
                 self.consume_expr(rhs);
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
index 786a8c28f99..b84c4918649 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
@@ -214,6 +214,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
             | ExprKind::Break(..)
             | ExprKind::Continue(..)
             | ExprKind::Ret(..)
+            | ExprKind::Become(..)
             | ExprKind::InlineAsm(..)
             | ExprKind::OffsetOf(..)
             | ExprKind::Struct(..)
@@ -451,6 +452,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
                 }
             }
 
+            ExprKind::Become(_call) => bug!("encountered a tail-call inside a generator"),
+
             ExprKind::Call(f, args) => {
                 self.visit_expr(f);
                 for arg in args {
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index 78171e0b20e..6c8589493cb 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -361,6 +361,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
             | hir::ExprKind::AssignOp(..)
             | hir::ExprKind::Closure { .. }
             | hir::ExprKind::Ret(..)
+            | hir::ExprKind::Become(..)
             | hir::ExprKind::Unary(..)
             | hir::ExprKind::Yield(..)
             | hir::ExprKind::MethodCall(..)
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 7f0c2e9ca3f..d33103b029e 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -695,6 +695,11 @@ impl<'tcx> Cx<'tcx> {
                 ExprKind::Repeat { value: self.mirror_expr(v), count: *count }
             }
             hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
+            hir::ExprKind::Become(call) => {
+                // FIXME(explicit_tail_calls): use `ExprKind::Become` once we implemented it
+                // Temporary transform `become` into a `return`, so we can write tests for code before this stage
+                ExprKind::Return { value: Some(self.mirror_expr(call)) }
+            }
             hir::ExprKind::Break(dest, ref value) => match dest.target_id {
                 Ok(target_id) => ExprKind::Break {
                     label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 90809270118..6c748147abe 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -302,8 +302,8 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
             [
                 ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type,
                 DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index,
-                Path, AddrOf, Break, Continue, Ret, InlineAsm, OffsetOf, Struct, Repeat, Yield,
-                Err
+                Path, AddrOf, Break, Continue, Ret, Become, InlineAsm, OffsetOf, Struct, Repeat,
+                Yield, Err
             ]
         );
         hir_visit::walk_expr(self, e)
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 63b1578d43f..803ca05b202 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -463,6 +463,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
             | hir::ExprKind::Lit(_)
             | hir::ExprKind::ConstBlock(..)
             | hir::ExprKind::Ret(..)
+            | hir::ExprKind::Become(..)
             | hir::ExprKind::Block(..)
             | hir::ExprKind::Assign(..)
             | hir::ExprKind::AssignOp(..)
@@ -967,6 +968,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 self.propagate_through_opt_expr(o_e.as_deref(), self.exit_ln)
             }
 
+            hir::ExprKind::Become(ref e) => {
+                // Ignore succ and subst exit_ln.
+                self.propagate_through_expr(e, self.exit_ln)
+            }
+
             hir::ExprKind::Break(label, ref opt_expr) => {
                 // Find which label this break jumps to
                 let target = match label.target_id {
@@ -1408,6 +1414,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
         | hir::ExprKind::DropTemps(..)
         | hir::ExprKind::Unary(..)
         | hir::ExprKind::Ret(..)
+        | hir::ExprKind::Become(..)
         | hir::ExprKind::Break(..)
         | hir::ExprKind::Continue(..)
         | hir::ExprKind::Lit(_)
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index a849d61edfe..769b389009b 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -204,6 +204,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
             | ExprKind::Continue(..)
             | ExprKind::Ret(..)
             | ExprKind::OffsetOf(..)
+            | ExprKind::Become(..)
             | ExprKind::Struct(..)
             | ExprKind::Repeat(..)
             | ExprKind::Yield(..) => {
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 5f1fdf00be8..10b5e1edf92 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -206,6 +206,12 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
                 NeverLoopResult::AlwaysBreak,
             )
         }),
+        ExprKind::Become(e) => {
+            combine_seq(
+                never_loop_expr(e, ignore_ids, main_loop_id),
+                NeverLoopResult::AlwaysBreak,
+            )
+        }
         ExprKind::InlineAsm(asm) => asm
             .operands
             .iter()
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 7945275393c..93ef07d36ae 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -329,6 +329,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
             ExprKind::Field(..) |
             ExprKind::Index(..) |
             ExprKind::Ret(..) |
+            ExprKind::Become(..) |
             ExprKind::Repeat(..) |
             ExprKind::Yield(..) => walk_expr(self, ex),
             ExprKind::AddrOf(_, _, _) |
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 3c2bf5abab2..6b51974d739 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -559,6 +559,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 kind!("Ret({value})");
                 value.if_some(|e| self.expr(e));
             },
+            ExprKind::Become(value) => {
+                bind!(self, value);
+                kind!("Become({value})");
+                self.expr(value);
+            },
             ExprKind::InlineAsm(_) => {
                 kind!("InlineAsm(_)");
                 out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index 941df3318ae..a42b2ccee88 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -191,6 +191,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 ExprKind::Break(..)
                 | ExprKind::Continue(_)
                 | ExprKind::Ret(_)
+                | ExprKind::Become(_)
                 | ExprKind::InlineAsm(_)
                 | ExprKind::Yield(..)
                 | ExprKind::Err(_) => {
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a49246a7832..3e1d7356414 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -845,6 +845,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     self.hash_expr(e);
                 }
             },
+            ExprKind::Become(f) => {
+                self.hash_expr(f);
+            },
             ExprKind::Path(ref qpath) => {
                 self.hash_qpath(qpath);
             },
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index a87d58110b0..b38b9553558 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -147,6 +147,7 @@ impl<'a> Sugg<'a> {
             | hir::ExprKind::Path(..)
             | hir::ExprKind::Repeat(..)
             | hir::ExprKind::Ret(..)
+            | hir::ExprKind::Become(..)
             | hir::ExprKind::Struct(..)
             | hir::ExprKind::Tup(..)
             | hir::ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)),
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 5dcd71cef12..8dafa723afa 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -651,6 +651,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
             // Either drops temporaries, jumps out of the current expression, or has no sub expression.
             ExprKind::DropTemps(_)
             | ExprKind::Ret(_)
+            | ExprKind::Become(_)
             | ExprKind::Break(..)
             | ExprKind::Yield(..)
             | ExprKind::Block(..)
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 55bf38110a6..3ed587caadc 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
 const ISSUES_ENTRY_LIMIT: usize = 1896;
-const ROOT_ENTRY_LIMIT: usize = 870;
+const ROOT_ENTRY_LIMIT: usize = 871;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/tests/ui/explicit-tail-calls/become-outside.array.stderr b/tests/ui/explicit-tail-calls/become-outside.array.stderr
new file mode 100644
index 00000000000..839c20509fe
--- /dev/null
+++ b/tests/ui/explicit-tail-calls/become-outside.array.stderr
@@ -0,0 +1,9 @@
+error[E0572]: become statement outside of function body
+  --> $DIR/become-outside.rs:11:17
+   |
+LL | struct Bad([(); become f()]);
+   |                 ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0572`.
diff --git a/tests/ui/explicit-tail-calls/become-outside.constant.stderr b/tests/ui/explicit-tail-calls/become-outside.constant.stderr
new file mode 100644
index 00000000000..9b67f08af3a
--- /dev/null
+++ b/tests/ui/explicit-tail-calls/become-outside.constant.stderr
@@ -0,0 +1,9 @@
+error[E0572]: become statement outside of function body
+  --> $DIR/become-outside.rs:7:5
+   |
+LL |     become f();
+   |     ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0572`.
diff --git a/tests/ui/explicit-tail-calls/become-outside.rs b/tests/ui/explicit-tail-calls/become-outside.rs
new file mode 100644
index 00000000000..51b4389c88f
--- /dev/null
+++ b/tests/ui/explicit-tail-calls/become-outside.rs
@@ -0,0 +1,15 @@
+// revisions: constant array
+#![allow(incomplete_features)]
+#![feature(explicit_tail_calls)]
+
+#[cfg(constant)]
+const _: () = {
+    become f(); //[constant]~ error: become statement outside of function body
+};
+
+#[cfg(array)]
+struct Bad([(); become f()]); //[array]~ error: become statement outside of function body
+
+fn f() {}
+
+fn main() {}
diff --git a/tests/ui/explicit-tail-calls/return-lifetime-sub.rs b/tests/ui/explicit-tail-calls/return-lifetime-sub.rs
new file mode 100644
index 00000000000..8a3f43d4b92
--- /dev/null
+++ b/tests/ui/explicit-tail-calls/return-lifetime-sub.rs
@@ -0,0 +1,13 @@
+// check-pass
+#![allow(incomplete_features)]
+#![feature(explicit_tail_calls)]
+
+fn _f<'a>() -> &'a [u8] {
+    become _g();
+}
+
+fn _g() -> &'static [u8] {
+    &[0, 1, 2, 3]
+}
+
+fn main() {}
diff --git a/tests/ui/explicit-tail-calls/return-mismatches.rs b/tests/ui/explicit-tail-calls/return-mismatches.rs
new file mode 100644
index 00000000000..935a1a1d28b
--- /dev/null
+++ b/tests/ui/explicit-tail-calls/return-mismatches.rs
@@ -0,0 +1,28 @@
+#![allow(incomplete_features)]
+#![feature(explicit_tail_calls)]
+
+fn _f0<'a>() -> &'static [u8] {
+    become _g0(); //~ error: mismatched types
+}
+
+fn _g0() -> &'static [u8; 1] {
+    &[0]
+}
+
+fn _f1() {
+    become _g1(); //~ error: mismatched types
+}
+
+fn _g1() -> ! {
+    become _g1();
+}
+
+fn _f2() -> u32 {
+    become _g2(); //~ error: mismatched types
+}
+
+fn _g2() -> u16 {
+    0
+}
+
+fn main() {}
diff --git a/tests/ui/explicit-tail-calls/return-mismatches.stderr b/tests/ui/explicit-tail-calls/return-mismatches.stderr
new file mode 100644
index 00000000000..1dcc35797c1
--- /dev/null
+++ b/tests/ui/explicit-tail-calls/return-mismatches.stderr
@@ -0,0 +1,27 @@
+error[E0308]: mismatched types
+  --> $DIR/return-mismatches.rs:5:5
+   |
+LL |     become _g0();
+   |     ^^^^^^^^^^^^ expected `&[u8]`, found `&[u8; 1]`
+   |
+   = note: expected reference `&'static [u8]`
+              found reference `&'static [u8; 1]`
+
+error[E0308]: mismatched types
+  --> $DIR/return-mismatches.rs:13:5
+   |
+LL |     become _g1();
+   |     ^^^^^^^^^^^^ expected `()`, found `!`
+   |
+   = note: expected unit type `()`
+                   found type `!`
+
+error[E0308]: mismatched types
+  --> $DIR/return-mismatches.rs:21:5
+   |
+LL |     become _g2();
+   |     ^^^^^^^^^^^^ expected `u32`, found `u16`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/issues/issue-51714.rs b/tests/ui/issues/issue-51714.rs
index 8716524d6f4..03b50b7963e 100644
--- a/tests/ui/issues/issue-51714.rs
+++ b/tests/ui/issues/issue-51714.rs
@@ -1,9 +1,9 @@
 fn main() {
-//~^ NOTE: not the enclosing function body
-//~| NOTE: not the enclosing function body
-//~| NOTE: not the enclosing function body
-//~| NOTE: not the enclosing function body
-    |_:  [_; return || {}] | {};
+    //~^ NOTE: not the enclosing function body
+    //~| NOTE: not the enclosing function body
+    //~| NOTE: not the enclosing function body
+    //~| NOTE: not the enclosing function body
+    |_: [_; return || {}]| {};
     //~^ ERROR: return statement outside of function body [E0572]
     //~| NOTE: the return is part of this body...
 
diff --git a/tests/ui/issues/issue-51714.stderr b/tests/ui/issues/issue-51714.stderr
index 514d69c1c7d..e53e10afcaf 100644
--- a/tests/ui/issues/issue-51714.stderr
+++ b/tests/ui/issues/issue-51714.stderr
@@ -1,13 +1,13 @@
 error[E0572]: return statement outside of function body
-  --> $DIR/issue-51714.rs:6:14
+  --> $DIR/issue-51714.rs:6:13
    |
 LL | / fn main() {
 LL | |
 LL | |
 LL | |
 LL | |
-LL | |     |_:  [_; return || {}] | {};
-   | |              ^^^^^^^^^^^^ the return is part of this body...
+LL | |     |_: [_; return || {}]| {};
+   | |             ^^^^^^^^^^^^ the return is part of this body...
 ...  |
 LL | |
 LL | | }
diff --git a/tests/ui/return/issue-64620.rs b/tests/ui/return/issue-64620.rs
index a62e5bf8d3c..ab293165195 100644
--- a/tests/ui/return/issue-64620.rs
+++ b/tests/ui/return/issue-64620.rs
@@ -1,5 +1,5 @@
 enum Bug {
-    V1 = return [0][0] //~ERROR return statement outside of function body
+    V1 = return [0][0], //~ERROR return statement outside of function body
 }
 
 fn main() {}
diff --git a/tests/ui/return/issue-64620.stderr b/tests/ui/return/issue-64620.stderr
index f40ac4de32d..3210a67d418 100644
--- a/tests/ui/return/issue-64620.stderr
+++ b/tests/ui/return/issue-64620.stderr
@@ -1,7 +1,7 @@
 error[E0572]: return statement outside of function body
   --> $DIR/issue-64620.rs:2:10
    |
-LL |     V1 = return [0][0]
+LL |     V1 = return [0][0],
    |          ^^^^^^^^^^^^^
 
 error: aborting due to previous error
diff --git a/tests/ui/return/issue-86188-return-not-in-fn-body.rs b/tests/ui/return/issue-86188-return-not-in-fn-body.rs
index 4f076fa0693..3117cf3fd91 100644
--- a/tests/ui/return/issue-86188-return-not-in-fn-body.rs
+++ b/tests/ui/return/issue-86188-return-not-in-fn-body.rs
@@ -7,7 +7,7 @@
 
 const C: [(); 42] = {
     [(); return || {
-    //~^ ERROR: return statement outside of function body [E0572]
+        //~^ ERROR: return statement outside of function body [E0572]
         let tx;
     }]
 };
@@ -16,7 +16,7 @@ struct S {}
 trait Tr {
     fn foo();
     fn bar() {
-    //~^ NOTE: ...not the enclosing function body
+        //~^ NOTE: ...not the enclosing function body
         [(); return];
         //~^ ERROR: return statement outside of function body [E0572]
         //~| NOTE: the return is part of this body...
@@ -24,7 +24,7 @@ trait Tr {
 }
 impl Tr for S {
     fn foo() {
-    //~^ NOTE: ...not the enclosing function body
+        //~^ NOTE: ...not the enclosing function body
         [(); return];
         //~^ ERROR: return statement outside of function body [E0572]
         //~| NOTE: the return is part of this body...
@@ -32,10 +32,10 @@ impl Tr for S {
 }
 
 fn main() {
-//~^ NOTE: ...not the enclosing function body
+    //~^ NOTE: ...not the enclosing function body
     [(); return || {
-    //~^ ERROR: return statement outside of function body [E0572]
-    //~| NOTE: the return is part of this body...
+        //~^ ERROR: return statement outside of function body [E0572]
+        //~| NOTE: the return is part of this body...
         let tx;
     }];
 }
diff --git a/tests/ui/return/tail-expr-as-potential-return.rs b/tests/ui/return/tail-expr-as-potential-return.rs
index f46e088b85f..2046d6680dd 100644
--- a/tests/ui/return/tail-expr-as-potential-return.rs
+++ b/tests/ui/return/tail-expr-as-potential-return.rs
@@ -1,8 +1,8 @@
-// > Suggest `return`ing tail expressions that match return type
+// > Suggest returning tail expressions that match return type
 // >
 // > Some newcomers are confused by the behavior of tail expressions,
 // > interpreting that "leaving out the `;` makes it the return value".
-// > To help them go in the right direction, suggest using `return` instead
+// > To help them go in the right direction, suggest using return instead
 // > when applicable.
 // (original commit description for this test)
 //
diff --git a/tests/ui/typeck/issue-86721-return-expr-ice.rs b/tests/ui/typeck/issue-86721-return-expr-ice.rs
index cd7135f18b1..4f882f7a3f1 100644
--- a/tests/ui/typeck/issue-86721-return-expr-ice.rs
+++ b/tests/ui/typeck/issue-86721-return-expr-ice.rs
@@ -2,7 +2,7 @@
 
 // revisions: rev1 rev2
 #![cfg_attr(any(), rev1, rev2)]
-#![crate_type="lib"]
+#![crate_type = "lib"]
 
 #[cfg(any(rev1))]
 trait T {