From 6f2c6efe019ef57585e1099e701c800417037e04 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 21 Mar 2024 19:47:46 +0000 Subject: [PATCH 1/2] Change syntax of the never type attribute thingy Previous: ```rust #![rustc_never_type_mode = "fallback_to_unit|..."] ``` New: ```rust #![rustc_never_type_options(fallback = "unit|...")] ``` This allows adding other options for other never-related experiments. --- compiler/rustc_feature/src/builtin_attrs.rs | 7 ++- compiler/rustc_hir_typeck/src/fallback.rs | 36 ++------------ compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 52 +++++++++++++++++++- compiler/rustc_span/src/symbol.rs | 8 ++- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d4d7833cb21..22cf50fce7f 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -597,9 +597,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( - rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|fallback_to_never|no_fallback"), ErrorFollowing, + rustc_never_type_options, + Normal, + template!(List: r#"/*opt*/ fallback = "unit|niko|never|no""#), + ErrorFollowing, EncodeCrossCrate::No, - "`rustc_never_type_fallback` is used to experiment with never type fallback and work on \ + "`rustc_never_type_options` is used to experiment with never type fallback and work on \ never type stabilization, and will never be stable" ), diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c16e941d4c5..140618e97cc 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -4,12 +4,11 @@ use rustc_data_structures::{ graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; -use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -enum DivergingFallbackBehavior { +#[derive(Copy, Clone)] +pub enum DivergingFallbackBehavior { /// Always fallback to `()` (aka "always spontaneous decay") FallbackToUnit, /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken. @@ -78,9 +77,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { return false; } - let diverging_behavior = self.diverging_fallback_behavior(); - let diverging_fallback = - self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior); + let diverging_fallback = self + .calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior); // We do fallback in two passes, to try to generate // better error messages. @@ -94,32 +92,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_occurred } - fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior { - let Some((mode, span)) = self - .tcx - .get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode) - .map(|attr| (attr.value_str().unwrap(), attr.span)) - else { - if self.tcx.features().never_type_fallback { - return DivergingFallbackBehavior::FallbackToNiko; - } - - return DivergingFallbackBehavior::FallbackToUnit; - }; - - match mode { - sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit, - sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko, - sym::fallback_to_never => DivergingFallbackBehavior::FallbackToNever, - sym::no_fallback => DivergingFallbackBehavior::NoFallback, - _ => { - self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, `fallback_to_never` and `no_fallback`)")); - - DivergingFallbackBehavior::FallbackToUnit - } - } - } - fn fallback_effects(&self) -> bool { let unsolved_effects = self.unsolved_effects(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 685b1af931e..fa948451d70 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,7 +5,9 @@ mod checks; mod suggestions; use crate::coercion::DynamicCoerceMany; +use crate::fallback::DivergingFallbackBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; +use hir::def_id::CRATE_DEF_ID; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -18,7 +20,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span, DUMMY_SP}; +use rustc_span::{self, sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -108,6 +110,8 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) inh: &'a Inherited<'tcx>, pub(super) fallback_has_occurred: Cell, + + pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -116,6 +120,7 @@ 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); FnCtxt { body_id, param_env, @@ -131,6 +136,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), inh, fallback_has_occurred: Cell::new(false), + diverging_fallback_behavior, } } @@ -374,3 +380,47 @@ impl<'tcx> LoweredTy<'tcx> { LoweredTy { raw, normalized } } } + +fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior { + 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 items = tcx + .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) + .map(|attr| attr.meta_item_list().unwrap()) + .unwrap_or_default(); + + for item in items { + if item.has_name(sym::fallback) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => fallback = Some(FallbackToUnit), + sym::niko => fallback = Some(FallbackToNiko), + sym::never => fallback = Some(FallbackToNever), + sym::no => fallback = Some(NoFallback), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)")); + } + }; + continue; + } + + tcx.dcx().span_err( + item.span(), + format!( + "unknown never type option: `{}` (supported: `fallback`)", + item.name_or_empty() + ), + ); + } + + let fallback = fallback.unwrap_or_else(|| { + if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } + }); + + fallback +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b911a41a11..77170406c06 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -814,9 +814,7 @@ symbols! { fadd_algebraic, fadd_fast, fake_variadic, - fallback_to_never, - fallback_to_niko, - fallback_to_unit, + fallback, fdiv_algebraic, fdiv_fast, feature, @@ -1227,6 +1225,7 @@ symbols! { new_v1, new_v1_formatted, next, + niko, nll, no, no_builtins, @@ -1235,7 +1234,6 @@ symbols! { no_crate_inject, no_debug, no_default_passes, - no_fallback, no_implicit_prelude, no_inline, no_link, @@ -1553,7 +1551,7 @@ symbols! { rustc_mir, rustc_must_implement_one_of, rustc_never_returns_null_ptr, - rustc_never_type_mode, + rustc_never_type_options, rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, From 93297bfb052a8dd72123547072872915f4d60675 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 21 Mar 2024 20:53:22 +0000 Subject: [PATCH 2/2] 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 5c56c6acd27..19f839b6240 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 (very-unstable-new-attribute) + /// #![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 fa948451d70..1850463194d 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 77170406c06..4fa8ba2aa05 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,