diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 64b816553dff9..3c591001a0d1a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -46,19 +46,18 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy)] pub enum DivergingBlockBehavior { - /// This is the current stable behavior: + /// This is edition <= 2021 behavior: /// /// ```rust /// { /// return; /// } // block has type = !, even though we are supposedly dropping it with `;` /// ``` - #[default] Never, - /// Alternative behavior: + /// This is edition >= 2024 behavior: /// /// ```ignore (very-unstable-new-attribute) /// #![rustc_never_type_options(diverging_block_default = "unit")] diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 05e7c5b2b4188..e57b5303887dc 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -441,7 +441,16 @@ fn parse_never_type_options_attr( if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } }); - let block = block.unwrap_or_default(); + let block = block.unwrap_or_else(|| default_diverging_block_behavior(tcx)); (fallback, block) } + +/// Returns the edition-based default diverging block behavior +fn default_diverging_block_behavior(tcx: TyCtxt<'_>) -> DivergingBlockBehavior { + if tcx.sess.edition().at_least_rust_2024() { + return DivergingBlockBehavior::Unit; + } + + DivergingBlockBehavior::Never +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 9a05fb1c30f8e..85b495982d92b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -714,6 +714,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?; let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { _ if last_expr_ty.references_error() => return None, + (ty::Never, _) => StatementAsExpression::CorrectType, _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => { StatementAsExpression::CorrectType } diff --git a/tests/ui/editions/diverging-block.e2024.fixed b/tests/ui/editions/diverging-block.e2024.fixed new file mode 100644 index 0000000000000..f36d41c3c4eaa --- /dev/null +++ b/tests/ui/editions/diverging-block.e2024.fixed @@ -0,0 +1,30 @@ +//@ revisions: e2021 e2024 +// +//@[e2021] edition: 2021 +//@[e2024] edition: 2024 +//@[e2024] compile-flags: -Zunstable-options +// +//@[e2021] check-pass +//@[e2024] check-fail +// +//@[e2024] run-rustfix + + +fn main() { + // a diverging block, with no tail expression. + // + // edition <= 2021: the block has type `!`, which then can be coerced. + // edition >= 2024: the block has type `()`, as with any block with no tail. + let _: u32 = { //[e2024]~ error: mismatched types + return + }; +} + +fn _f() { + // Same as the above, but with an if + if true { + return + } else { + 0_u32 //[e2024]~ error: `if` and `else` have incompatible types + }; +} diff --git a/tests/ui/editions/diverging-block.e2024.stderr b/tests/ui/editions/diverging-block.e2024.stderr new file mode 100644 index 0000000000000..6380a8648b29e --- /dev/null +++ b/tests/ui/editions/diverging-block.e2024.stderr @@ -0,0 +1,28 @@ +error[E0308]: mismatched types + --> $DIR/diverging-block.rs:18:18 + | +LL | let _: u32 = { + | __________________^ +LL | | return; + | | - help: remove this semicolon to return this value +LL | | }; + | |_____^ expected `u32`, found `()` + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/diverging-block.rs:28:9 + | +LL | / if true { +LL | | return; + | | ------- + | | | | + | | | help: consider removing this semicolon + | | expected because of this +LL | | } else { +LL | | 0_u32 + | | ^^^^^ expected `()`, found `u32` +LL | | }; + | |_____- `if` and `else` have incompatible types + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/editions/diverging-block.rs b/tests/ui/editions/diverging-block.rs new file mode 100644 index 0000000000000..676d7da29f325 --- /dev/null +++ b/tests/ui/editions/diverging-block.rs @@ -0,0 +1,30 @@ +//@ revisions: e2021 e2024 +// +//@[e2021] edition: 2021 +//@[e2024] edition: 2024 +//@[e2024] compile-flags: -Zunstable-options +// +//@[e2021] check-pass +//@[e2024] check-fail +// +//@[e2024] run-rustfix + + +fn main() { + // a diverging block, with no tail expression. + // + // edition <= 2021: the block has type `!`, which then can be coerced. + // edition >= 2024: the block has type `()`, as with any block with no tail. + let _: u32 = { //[e2024]~ error: mismatched types + return; + }; +} + +fn _f() { + // Same as the above, but with an if + if true { + return; + } else { + 0_u32 //[e2024]~ error: `if` and `else` have incompatible types + }; +}