From 2af16db0af0481aa7330cdf4475328e58be3de01 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 21 Mar 2024 20:53:22 +0000 Subject: [PATCH] Add a never type option to make diverging blocks `()` ```rust #![allow(internal_features)] #![feature(never_type, rustc_attrs)] #![rustc_never_type_options(diverging_block_default = "unit")] fn main() { let _: u8 = { //~ error: expected `u8`, found `()` return; }; } ``` --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 27 ++++++++++++++++++- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 27 ++++++++++++++++--- compiler/rustc_span/src/symbol.rs | 1 + 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 5c56c6acd2791..7ff900c5dde4c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -45,6 +45,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; +#[derive(Clone, Copy, Default)] +pub enum DivergingBlockBehavior { + /// This is the current stable behavior: + /// + /// ```rust + /// { + /// return; + /// } // block has type = !, even though we are supposedly dropping it with `;` + /// ``` + #[default] + Never, + + /// Alternative behavior: + /// + /// ```ignore + /// #![rustc_never_type_options(diverging_block_default = "unit")] + /// { + /// return; + /// } // block has type = (), since we are dropping `!` from `return` with `;` + /// ``` + Unit, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors @@ -1710,7 +1733,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // #41425 -- label the implicit `()` as being the // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { + if !self.diverges.get().is_always() + || matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit) + { // #50009 -- Do not point at the entire fn block span, point at the return type // span, as it is the cause of the requirement, and // `consider_hint_about_removing_semicolon` will point at the last expression diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index fa948451d70ae..1850463194dc8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -6,6 +6,7 @@ mod suggestions; use crate::coercion::DynamicCoerceMany; use crate::fallback::DivergingFallbackBehavior; +use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; use hir::def_id::CRATE_DEF_ID; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; @@ -112,6 +113,7 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) fallback_has_occurred: Cell, pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, + pub(super) diverging_block_behavior: DivergingBlockBehavior, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -120,7 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { - let diverging_fallback_behavior = parse_never_type_options_attr(inh.tcx); + let (diverging_fallback_behavior, diverging_block_behavior) = + parse_never_type_options_attr(inh.tcx); FnCtxt { body_id, param_env, @@ -137,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inh, fallback_has_occurred: Cell::new(false), diverging_fallback_behavior, + diverging_block_behavior, } } @@ -381,13 +385,16 @@ impl<'tcx> LoweredTy<'tcx> { } } -fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { +fn parse_never_type_options_attr( + tcx: TyCtxt<'_>, +) -> (DivergingFallbackBehavior, DivergingBlockBehavior) { use DivergingFallbackBehavior::*; // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. // Just don't write incorrect attributes <3 let mut fallback = None; + let mut block = None; let items = tcx .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) @@ -409,6 +416,18 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { continue; } + if item.has_name(sym::diverging_block_default) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => block = Some(DivergingBlockBehavior::Unit), + sym::never => block = Some(DivergingBlockBehavior::Never), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)")); + } + }; + continue; + } + tcx.dcx().span_err( item.span(), format!( @@ -422,5 +441,7 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } }); - fallback + let block = block.unwrap_or_default(); + + (fallback, block) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 77170406c0602..4fa8ba2aa050e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -689,6 +689,7 @@ symbols! { dispatch_from_dyn, div, div_assign, + diverging_block_default, do_not_recommend, doc, doc_alias,