Skip to content

Commit 6f8c27a

Browse files
committed
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`
2 parents 27e10c5 + e38576a commit 6f8c27a

35 files changed

+286
-72
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
277277
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
278278
ExprKind::Become(sub_expr) => {
279279
let sub_expr = self.lower_expr(sub_expr);
280-
281-
// FIXME(explicit_tail_calls): Use `hir::ExprKind::Become` once we implemented it
282-
hir::ExprKind::Ret(Some(sub_expr))
280+
hir::ExprKind::Become(sub_expr)
283281
}
284282
ExprKind::InlineAsm(asm) => {
285283
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))

compiler/rustc_hir/src/hir.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,7 @@ impl Expr<'_> {
17191719
ExprKind::Break(..) => ExprPrecedence::Break,
17201720
ExprKind::Continue(..) => ExprPrecedence::Continue,
17211721
ExprKind::Ret(..) => ExprPrecedence::Ret,
1722+
ExprKind::Become(..) => ExprPrecedence::Become,
17221723
ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
17231724
ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf,
17241725
ExprKind::Struct(..) => ExprPrecedence::Struct,
@@ -1776,6 +1777,7 @@ impl Expr<'_> {
17761777
| ExprKind::Break(..)
17771778
| ExprKind::Continue(..)
17781779
| ExprKind::Ret(..)
1780+
| ExprKind::Become(..)
17791781
| ExprKind::Let(..)
17801782
| ExprKind::Loop(..)
17811783
| ExprKind::Assign(..)
@@ -1866,6 +1868,7 @@ impl Expr<'_> {
18661868
| ExprKind::Break(..)
18671869
| ExprKind::Continue(..)
18681870
| ExprKind::Ret(..)
1871+
| ExprKind::Become(..)
18691872
| ExprKind::Let(..)
18701873
| ExprKind::Loop(..)
18711874
| ExprKind::Assign(..)
@@ -2025,6 +2028,8 @@ pub enum ExprKind<'hir> {
20252028
Continue(Destination),
20262029
/// A `return`, with an optional value to be returned.
20272030
Ret(Option<&'hir Expr<'hir>>),
2031+
/// A `become`, with the value to be returned.
2032+
Become(&'hir Expr<'hir>),
20282033

20292034
/// Inline assembly (from `asm!`), with its outputs and inputs.
20302035
InlineAsm(&'hir InlineAsm<'hir>),

compiler/rustc_hir/src/intravisit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
791791
ExprKind::Ret(ref optional_expression) => {
792792
walk_list!(visitor, visit_expr, optional_expression);
793793
}
794+
ExprKind::Become(ref expr) => visitor.visit_expr(expr),
794795
ExprKind::InlineAsm(ref asm) => {
795796
visitor.visit_inline_asm(asm, expression.hir_id);
796797
}

compiler/rustc_hir_pretty/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,11 @@ impl<'a> State<'a> {
15541554
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
15551555
}
15561556
}
1557+
hir::ExprKind::Become(result) => {
1558+
self.word("become");
1559+
self.word(" ");
1560+
self.print_expr_maybe_paren(result, parser::PREC_JUMP);
1561+
}
15571562
hir::ExprKind::InlineAsm(asm) => {
15581563
self.word("asm!");
15591564
self.print_inline_asm(asm);

compiler/rustc_hir_typeck/messages.ftl

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang
7878
hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
7979
8080
hir_typeck_return_stmt_outside_of_fn_body =
81-
return statement outside of function body
82-
.encl_body_label = the return is part of this body...
81+
{$statement_kind} statement outside of function body
82+
.encl_body_label = the {$statement_kind} is part of this body...
8383
.encl_fn_label = ...not the enclosing function body
8484
8585
hir_typeck_struct_expr_non_exhaustive =

compiler/rustc_hir_typeck/src/errors.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
use std::borrow::Cow;
33

44
use crate::fluent_generated as fluent;
5-
use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage};
5+
use rustc_errors::{
6+
AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, MultiSpan,
7+
SubdiagnosticMessage,
8+
};
69
use rustc_macros::{Diagnostic, Subdiagnostic};
710
use rustc_middle::ty::Ty;
811
use rustc_span::{
@@ -31,6 +34,24 @@ pub struct ReturnStmtOutsideOfFnBody {
3134
pub encl_body_span: Option<Span>,
3235
#[label(hir_typeck_encl_fn_label)]
3336
pub encl_fn_span: Option<Span>,
37+
pub statement_kind: ReturnLikeStatementKind,
38+
}
39+
40+
pub enum ReturnLikeStatementKind {
41+
Return,
42+
Become,
43+
}
44+
45+
impl IntoDiagnosticArg for ReturnLikeStatementKind {
46+
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
47+
let kind = match self {
48+
Self::Return => "return",
49+
Self::Become => "become",
50+
}
51+
.into();
52+
53+
DiagnosticArgValue::Str(kind)
54+
}
3455
}
3556

3657
#[derive(Diagnostic)]

compiler/rustc_hir_typeck/src/expr.rs

+88-44
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use crate::cast;
66
use crate::coercion::CoerceMany;
77
use crate::coercion::DynamicCoerceMany;
8+
use crate::errors::ReturnLikeStatementKind;
89
use crate::errors::TypeMismatchFruTypo;
910
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
1011
use crate::errors::{
@@ -324,6 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
324325
}
325326
}
326327
ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
328+
ExprKind::Become(call) => self.check_expr_become(call, expr),
327329
ExprKind::Let(let_expr) => self.check_expr_let(let_expr),
328330
ExprKind::Loop(body, _, source, _) => {
329331
self.check_expr_loop(body, source, expected, expr)
@@ -735,47 +737,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
735737
expr: &'tcx hir::Expr<'tcx>,
736738
) -> Ty<'tcx> {
737739
if self.ret_coercion.is_none() {
738-
let mut err = ReturnStmtOutsideOfFnBody {
739-
span: expr.span,
740-
encl_body_span: None,
741-
encl_fn_span: None,
742-
};
743-
744-
let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
745-
746-
if let Some(hir::Node::Item(hir::Item {
747-
kind: hir::ItemKind::Fn(..),
748-
span: encl_fn_span,
749-
..
750-
}))
751-
| Some(hir::Node::TraitItem(hir::TraitItem {
752-
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
753-
span: encl_fn_span,
754-
..
755-
}))
756-
| Some(hir::Node::ImplItem(hir::ImplItem {
757-
kind: hir::ImplItemKind::Fn(..),
758-
span: encl_fn_span,
759-
..
760-
})) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
761-
{
762-
// We are inside a function body, so reporting "return statement
763-
// outside of function body" needs an explanation.
764-
765-
let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
766-
767-
// If this didn't hold, we would not have to report an error in
768-
// the first place.
769-
assert_ne!(encl_item_id.def_id, encl_body_owner_id);
770-
771-
let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
772-
let encl_body = self.tcx.hir().body(encl_body_id);
773-
774-
err.encl_body_span = Some(encl_body.value.span);
775-
err.encl_fn_span = Some(*encl_fn_span);
776-
}
777-
778-
self.tcx.sess.emit_err(err);
740+
self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
779741

780742
if let Some(e) = expr_opt {
781743
// We still have to type-check `e` (issue #86188), but calling
@@ -815,6 +777,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
815777
self.tcx.types.never
816778
}
817779

780+
fn check_expr_become(
781+
&self,
782+
call: &'tcx hir::Expr<'tcx>,
783+
expr: &'tcx hir::Expr<'tcx>,
784+
) -> Ty<'tcx> {
785+
match &self.ret_coercion {
786+
Some(ret_coercion) => {
787+
let ret_ty = ret_coercion.borrow().expected_ty();
788+
let call_expr_ty = self.check_expr_with_hint(call, ret_ty);
789+
790+
// N.B. don't coerce here, as tail calls can't support most/all coercions
791+
// FIXME(explicit_tail_calls): add a diagnostic note that `become` doesn't allow coercions
792+
self.demand_suptype(expr.span, ret_ty, call_expr_ty);
793+
}
794+
None => {
795+
self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Become);
796+
797+
// Fallback to simply type checking `call` without hint/demanding the right types.
798+
// Best effort to highlight more errors.
799+
self.check_expr(call);
800+
}
801+
}
802+
803+
self.tcx.types.never
804+
}
805+
806+
/// Check an expression that _is being returned_.
807+
/// For example, this is called with `return_expr: $expr` when `return $expr`
808+
/// is encountered.
809+
///
810+
/// Note that this function must only be called in function bodies.
811+
///
818812
/// `explicit_return` is `true` if we're checking an explicit `return expr`,
819813
/// and `false` if we're checking a trailing expression.
820814
pub(super) fn check_return_expr(
@@ -831,10 +825,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
831825
let mut span = return_expr.span;
832826
// Use the span of the trailing expression for our cause,
833827
// not the span of the entire function
834-
if !explicit_return {
835-
if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr {
828+
if !explicit_return
829+
&& let ExprKind::Block(body, _) = return_expr.kind
830+
&& let Some(last_expr) = body.expr
831+
{
836832
span = last_expr.span;
837-
}
838833
}
839834
ret_coercion.borrow_mut().coerce(
840835
self,
@@ -854,6 +849,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
854849
}
855850
}
856851

852+
/// Emit an error because `return` or `become` is used outside of a function body.
853+
///
854+
/// `expr` is the `return` (`become`) "statement", `kind` is the kind of the statement
855+
/// either `Return` or `Become`.
856+
fn emit_return_outside_of_fn_body(&self, expr: &hir::Expr<'_>, kind: ReturnLikeStatementKind) {
857+
let mut err = ReturnStmtOutsideOfFnBody {
858+
span: expr.span,
859+
encl_body_span: None,
860+
encl_fn_span: None,
861+
statement_kind: kind,
862+
};
863+
864+
let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
865+
866+
if let Some(hir::Node::Item(hir::Item {
867+
kind: hir::ItemKind::Fn(..),
868+
span: encl_fn_span,
869+
..
870+
}))
871+
| Some(hir::Node::TraitItem(hir::TraitItem {
872+
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
873+
span: encl_fn_span,
874+
..
875+
}))
876+
| Some(hir::Node::ImplItem(hir::ImplItem {
877+
kind: hir::ImplItemKind::Fn(..),
878+
span: encl_fn_span,
879+
..
880+
})) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
881+
{
882+
// We are inside a function body, so reporting "return statement
883+
// outside of function body" needs an explanation.
884+
885+
let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
886+
887+
// If this didn't hold, we would not have to report an error in
888+
// the first place.
889+
assert_ne!(encl_item_id.def_id, encl_body_owner_id);
890+
891+
let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
892+
let encl_body = self.tcx.hir().body(encl_body_id);
893+
894+
err.encl_body_span = Some(encl_body.value.span);
895+
err.encl_fn_span = Some(*encl_fn_span);
896+
}
897+
898+
self.tcx.sess.emit_err(err);
899+
}
900+
857901
fn point_at_return_for_opaque_ty_error(
858902
&self,
859903
errors: &mut Vec<traits::FulfillmentError<'tcx>>,

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+4
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
326326
}
327327
}
328328

329+
hir::ExprKind::Become(call) => {
330+
self.consume_expr(call);
331+
}
332+
329333
hir::ExprKind::Assign(lhs, rhs, _) => {
330334
self.mutate_expr(lhs);
331335
self.consume_expr(rhs);

compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs

+3
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
214214
| ExprKind::Break(..)
215215
| ExprKind::Continue(..)
216216
| ExprKind::Ret(..)
217+
| ExprKind::Become(..)
217218
| ExprKind::InlineAsm(..)
218219
| ExprKind::OffsetOf(..)
219220
| ExprKind::Struct(..)
@@ -451,6 +452,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
451452
}
452453
}
453454

455+
ExprKind::Become(_call) => bug!("encountered a tail-call inside a generator"),
456+
454457
ExprKind::Call(f, args) => {
455458
self.visit_expr(f);
456459
for arg in args {

compiler/rustc_hir_typeck/src/mem_categorization.rs

+1
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
361361
| hir::ExprKind::AssignOp(..)
362362
| hir::ExprKind::Closure { .. }
363363
| hir::ExprKind::Ret(..)
364+
| hir::ExprKind::Become(..)
364365
| hir::ExprKind::Unary(..)
365366
| hir::ExprKind::Yield(..)
366367
| hir::ExprKind::MethodCall(..)

compiler/rustc_mir_build/src/thir/cx/expr.rs

+5
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,11 @@ impl<'tcx> Cx<'tcx> {
695695
ExprKind::Repeat { value: self.mirror_expr(v), count: *count }
696696
}
697697
hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
698+
hir::ExprKind::Become(call) => {
699+
// FIXME(explicit_tail_calls): use `ExprKind::Become` once we implemented it
700+
// Temporary transform `become` into a `return`, so we can write tests for code before this stage
701+
ExprKind::Return { value: Some(self.mirror_expr(call)) }
702+
}
698703
hir::ExprKind::Break(dest, ref value) => match dest.target_id {
699704
Ok(target_id) => ExprKind::Break {
700705
label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },

compiler/rustc_passes/src/hir_stats.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
302302
[
303303
ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type,
304304
DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index,
305-
Path, AddrOf, Break, Continue, Ret, InlineAsm, OffsetOf, Struct, Repeat, Yield,
306-
Err
305+
Path, AddrOf, Break, Continue, Ret, Become, InlineAsm, OffsetOf, Struct, Repeat,
306+
Yield, Err
307307
]
308308
);
309309
hir_visit::walk_expr(self, e)

compiler/rustc_passes/src/liveness.rs

+7
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
463463
| hir::ExprKind::Lit(_)
464464
| hir::ExprKind::ConstBlock(..)
465465
| hir::ExprKind::Ret(..)
466+
| hir::ExprKind::Become(..)
466467
| hir::ExprKind::Block(..)
467468
| hir::ExprKind::Assign(..)
468469
| hir::ExprKind::AssignOp(..)
@@ -967,6 +968,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
967968
self.propagate_through_opt_expr(o_e.as_deref(), self.exit_ln)
968969
}
969970

971+
hir::ExprKind::Become(ref e) => {
972+
// Ignore succ and subst exit_ln.
973+
self.propagate_through_expr(e, self.exit_ln)
974+
}
975+
970976
hir::ExprKind::Break(label, ref opt_expr) => {
971977
// Find which label this break jumps to
972978
let target = match label.target_id {
@@ -1408,6 +1414,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
14081414
| hir::ExprKind::DropTemps(..)
14091415
| hir::ExprKind::Unary(..)
14101416
| hir::ExprKind::Ret(..)
1417+
| hir::ExprKind::Become(..)
14111418
| hir::ExprKind::Break(..)
14121419
| hir::ExprKind::Continue(..)
14131420
| hir::ExprKind::Lit(_)

compiler/rustc_passes/src/naked_functions.rs

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
204204
| ExprKind::Continue(..)
205205
| ExprKind::Ret(..)
206206
| ExprKind::OffsetOf(..)
207+
| ExprKind::Become(..)
207208
| ExprKind::Struct(..)
208209
| ExprKind::Repeat(..)
209210
| ExprKind::Yield(..) => {

src/tools/clippy/clippy_lints/src/loops/never_loop.rs

+6
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
206206
NeverLoopResult::AlwaysBreak,
207207
)
208208
}),
209+
ExprKind::Become(e) => {
210+
combine_seq(
211+
never_loop_expr(e, ignore_ids, main_loop_id),
212+
NeverLoopResult::AlwaysBreak,
213+
)
214+
}
209215
ExprKind::InlineAsm(asm) => asm
210216
.operands
211217
.iter()

0 commit comments

Comments
 (0)