From ccb71ff424a37f0a52111bc0884b0df282dbcb6d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Mon, 21 Nov 2022 12:40:27 +0000 Subject: [PATCH 1/2] `hir`: Add `Become` expression kind --- compiler/rustc_ast_lowering/src/expr.rs | 4 +- compiler/rustc_hir/src/hir.rs | 5 + compiler/rustc_hir/src/intravisit.rs | 1 + compiler/rustc_hir_pretty/src/lib.rs | 5 + compiler/rustc_hir_typeck/messages.ftl | 4 +- compiler/rustc_hir_typeck/src/errors.rs | 23 ++- compiler/rustc_hir_typeck/src/expr.rs | 132 ++++++++++++------ .../rustc_hir_typeck/src/expr_use_visitor.rs | 4 + .../drop_ranges/cfg_build.rs | 3 + .../src/mem_categorization.rs | 1 + compiler/rustc_mir_build/src/thir/cx/expr.rs | 5 + compiler/rustc_passes/src/hir_stats.rs | 4 +- compiler/rustc_passes/src/liveness.rs | 7 + compiler/rustc_passes/src/naked_functions.rs | 1 + src/tools/tidy/src/ui_tests.rs | 2 +- .../become-outside.array.stderr | 9 ++ .../become-outside.constant.stderr | 9 ++ .../ui/explicit-tail-calls/become-outside.rs | 15 ++ .../return-lifetime-sub.rs | 13 ++ .../explicit-tail-calls/return-mismatches.rs | 28 ++++ .../return-mismatches.stderr | 27 ++++ tests/ui/issues/issue-51714.rs | 10 +- tests/ui/issues/issue-51714.stderr | 6 +- tests/ui/return/issue-64620.rs | 2 +- tests/ui/return/issue-64620.stderr | 2 +- .../issue-86188-return-not-in-fn-body.rs | 12 +- .../return/tail-expr-as-potential-return.rs | 4 +- .../ui/typeck/issue-86721-return-expr-ice.rs | 2 +- 28 files changed, 268 insertions(+), 72 deletions(-) create mode 100644 tests/ui/explicit-tail-calls/become-outside.array.stderr create mode 100644 tests/ui/explicit-tail-calls/become-outside.constant.stderr create mode 100644 tests/ui/explicit-tail-calls/become-outside.rs create mode 100644 tests/ui/explicit-tail-calls/return-lifetime-sub.rs create mode 100644 tests/ui/explicit-tail-calls/return-mismatches.rs create mode 100644 tests/ui/explicit-tail-calls/return-mismatches.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 225714a13615f..29972dd76eb0f 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 70fc66947df99..df495d5c42eaa 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 f84c814bd9278..1886a91bda838 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 ced46fe426c47..a699cd6c942eb 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 c1c58db57648c..3d012a15a675b 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 6b4168d89446f..05906a4b9f598 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, #[label(hir_typeck_encl_fn_label)] pub encl_fn_span: Option, + 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 e250e68a7f176..8d621c5a42b25 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>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 82d9f03b1451e..0d2e0602e1cdd 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 786a8c28f998b..b84c49186495d 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 78171e0b20e8c..6c8589493cb01 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 7f0c2e9ca3fb2..d33103b029e5b 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 90809270118d0..6c748147abe07 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 63b1578d43fd7..803ca05b20259 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 a849d61edfeaa..769b389009b7e 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/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 55bf38110a6d5..3ed587caadc6b 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 0000000000000..839c20509fe73 --- /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 0000000000000..9b67f08af3aea --- /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 0000000000000..51b4389c88fcd --- /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 0000000000000..8a3f43d4b92b5 --- /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 0000000000000..935a1a1d28b02 --- /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 0000000000000..1dcc35797c130 --- /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 8716524d6f4b5..03b50b7963ea5 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 514d69c1c7d39..e53e10afcafaa 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 a62e5bf8d3c62..ab293165195fd 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 f40ac4de32d59..3210a67d41805 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 4f076fa069383..3117cf3fd910d 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 f46e088b85f72..2046d6680dd25 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 cd7135f18b112..4f882f7a3f1e1 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 { From e38576a893b30d8f2a71665e28498219dcc907e3 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 21 Jun 2023 14:00:51 +0000 Subject: [PATCH 2/2] Support `hir::ExprKind::Become` in clippy --- src/tools/clippy/clippy_lints/src/loops/never_loop.rs | 6 ++++++ .../src/matches/significant_drop_in_scrutinee.rs | 1 + src/tools/clippy/clippy_lints/src/utils/author.rs | 5 +++++ src/tools/clippy/clippy_utils/src/eager_or_lazy.rs | 1 + src/tools/clippy/clippy_utils/src/hir_utils.rs | 3 +++ src/tools/clippy/clippy_utils/src/sugg.rs | 1 + src/tools/clippy/clippy_utils/src/visitors.rs | 1 + 7 files changed, 18 insertions(+) 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 5f1fdf00be8c3..10b5e1edf9250 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, 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 7945275393c04..93ef07d36aea7 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 3c2bf5abab2b5..6b51974d739af 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 941df3318ae8b..a42b2ccee889a 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 a49246a783272..3e1d735641468 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 a87d58110b0c1..b38b9553558c8 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 5dcd71cef127e..8dafa723afa00 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(..)