Rollup merge of #122843 - WaffleLapkin:semicolon-vs-the-never, r=compiler-errors
Add a never type option to make diverging blocks `()` More experiments for ~~the blood god~~ T-lang! Usage example: ```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; }; } ``` r? compiler-errors I'm not sure how I feel about parsing the attribute every time we create `FnCtxt`. There must be a better way to do this, right?
This commit is contained in:
commit
b469a6dd9b
5 changed files with 111 additions and 41 deletions
|
@ -597,9 +597,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||||
),
|
),
|
||||||
|
|
||||||
rustc_attr!(
|
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,
|
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"
|
never type stabilization, and will never be stable"
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,11 @@ use rustc_data_structures::{
|
||||||
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
|
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
|
||||||
unord::{UnordBag, UnordMap, UnordSet},
|
unord::{UnordBag, UnordMap, UnordSet},
|
||||||
};
|
};
|
||||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
|
||||||
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
||||||
use rustc_middle::ty::{self, Ty};
|
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")
|
/// Always fallback to `()` (aka "always spontaneous decay")
|
||||||
FallbackToUnit,
|
FallbackToUnit,
|
||||||
/// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
|
/// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let diverging_behavior = self.diverging_fallback_behavior();
|
let diverging_fallback = self
|
||||||
let diverging_fallback =
|
.calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior);
|
||||||
self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior);
|
|
||||||
|
|
||||||
// We do fallback in two passes, to try to generate
|
// We do fallback in two passes, to try to generate
|
||||||
// better error messages.
|
// better error messages.
|
||||||
|
@ -94,32 +92,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||||
fallback_occurred
|
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 {
|
fn fallback_effects(&self) -> bool {
|
||||||
let unsolved_effects = self.unsolved_effects();
|
let unsolved_effects = self.unsolved_effects();
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::mem;
|
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> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
pub(in super::super) fn check_casts(&mut self) {
|
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
|
// 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
|
// #41425 -- label the implicit `()` as being the
|
||||||
// "found type" here, rather than the "expected type".
|
// "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
|
// #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
|
// span, as it is the cause of the requirement, and
|
||||||
// `consider_hint_about_removing_semicolon` will point at the last expression
|
// `consider_hint_about_removing_semicolon` will point at the last expression
|
||||||
|
|
|
@ -5,7 +5,10 @@ mod checks;
|
||||||
mod suggestions;
|
mod suggestions;
|
||||||
|
|
||||||
use crate::coercion::DynamicCoerceMany;
|
use crate::coercion::DynamicCoerceMany;
|
||||||
|
use crate::fallback::DivergingFallbackBehavior;
|
||||||
|
use crate::fn_ctxt::checks::DivergingBlockBehavior;
|
||||||
use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited};
|
use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited};
|
||||||
|
use hir::def_id::CRATE_DEF_ID;
|
||||||
use rustc_errors::{DiagCtxt, ErrorGuaranteed};
|
use rustc_errors::{DiagCtxt, ErrorGuaranteed};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
|
@ -18,7 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
|
||||||
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::Ident;
|
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 rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
@ -108,6 +111,9 @@ pub struct FnCtxt<'a, 'tcx> {
|
||||||
pub(super) inh: &'a Inherited<'tcx>,
|
pub(super) inh: &'a Inherited<'tcx>,
|
||||||
|
|
||||||
pub(super) fallback_has_occurred: Cell<bool>,
|
pub(super) fallback_has_occurred: Cell<bool>,
|
||||||
|
|
||||||
|
pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
|
||||||
|
pub(super) diverging_block_behavior: DivergingBlockBehavior,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
@ -116,6 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
body_id: LocalDefId,
|
body_id: LocalDefId,
|
||||||
) -> FnCtxt<'a, 'tcx> {
|
) -> FnCtxt<'a, 'tcx> {
|
||||||
|
let (diverging_fallback_behavior, diverging_block_behavior) =
|
||||||
|
parse_never_type_options_attr(inh.tcx);
|
||||||
FnCtxt {
|
FnCtxt {
|
||||||
body_id,
|
body_id,
|
||||||
param_env,
|
param_env,
|
||||||
|
@ -131,6 +139,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}),
|
}),
|
||||||
inh,
|
inh,
|
||||||
fallback_has_occurred: Cell::new(false),
|
fallback_has_occurred: Cell::new(false),
|
||||||
|
diverging_fallback_behavior,
|
||||||
|
diverging_block_behavior,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,3 +384,64 @@ impl<'tcx> LoweredTy<'tcx> {
|
||||||
LoweredTy { raw, normalized }
|
LoweredTy { raw, normalized }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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!(
|
||||||
|
"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 }
|
||||||
|
});
|
||||||
|
|
||||||
|
let block = block.unwrap_or_default();
|
||||||
|
|
||||||
|
(fallback, block)
|
||||||
|
}
|
||||||
|
|
|
@ -691,6 +691,7 @@ symbols! {
|
||||||
dispatch_from_dyn,
|
dispatch_from_dyn,
|
||||||
div,
|
div,
|
||||||
div_assign,
|
div_assign,
|
||||||
|
diverging_block_default,
|
||||||
do_not_recommend,
|
do_not_recommend,
|
||||||
doc,
|
doc,
|
||||||
doc_alias,
|
doc_alias,
|
||||||
|
@ -816,9 +817,7 @@ symbols! {
|
||||||
fadd_algebraic,
|
fadd_algebraic,
|
||||||
fadd_fast,
|
fadd_fast,
|
||||||
fake_variadic,
|
fake_variadic,
|
||||||
fallback_to_never,
|
fallback,
|
||||||
fallback_to_niko,
|
|
||||||
fallback_to_unit,
|
|
||||||
fdiv_algebraic,
|
fdiv_algebraic,
|
||||||
fdiv_fast,
|
fdiv_fast,
|
||||||
feature,
|
feature,
|
||||||
|
@ -1230,6 +1229,7 @@ symbols! {
|
||||||
new_v1,
|
new_v1,
|
||||||
new_v1_formatted,
|
new_v1_formatted,
|
||||||
next,
|
next,
|
||||||
|
niko,
|
||||||
nll,
|
nll,
|
||||||
no,
|
no,
|
||||||
no_builtins,
|
no_builtins,
|
||||||
|
@ -1238,7 +1238,6 @@ symbols! {
|
||||||
no_crate_inject,
|
no_crate_inject,
|
||||||
no_debug,
|
no_debug,
|
||||||
no_default_passes,
|
no_default_passes,
|
||||||
no_fallback,
|
|
||||||
no_implicit_prelude,
|
no_implicit_prelude,
|
||||||
no_inline,
|
no_inline,
|
||||||
no_link,
|
no_link,
|
||||||
|
@ -1556,7 +1555,7 @@ symbols! {
|
||||||
rustc_mir,
|
rustc_mir,
|
||||||
rustc_must_implement_one_of,
|
rustc_must_implement_one_of,
|
||||||
rustc_never_returns_null_ptr,
|
rustc_never_returns_null_ptr,
|
||||||
rustc_never_type_mode,
|
rustc_never_type_options,
|
||||||
rustc_no_mir_inline,
|
rustc_no_mir_inline,
|
||||||
rustc_nonnull_optimization_guaranteed,
|
rustc_nonnull_optimization_guaranteed,
|
||||||
rustc_nounwind,
|
rustc_nounwind,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue