Add rustc_never_type_mode
crate-level attribute to allow experimenting
This commit is contained in:
parent
ee03c286cf
commit
19bc337063
@ -580,6 +580,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
"`may_dangle` has unstable semantics and may be removed in the future",
|
"`may_dangle` has unstable semantics and may be removed in the future",
|
||||||
),
|
),
|
||||||
|
|
||||||
|
rustc_attr!(
|
||||||
|
rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|no_fallback"), ErrorFollowing,
|
||||||
|
@only_local: true,
|
||||||
|
"`rustc_never_type_fallback` is used to experiment with never type fallback and work on \
|
||||||
|
never type stabilization, and will never be stable"
|
||||||
|
),
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Internal attributes: Runtime related:
|
// Internal attributes: Runtime related:
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
@ -4,8 +4,19 @@ 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 {
|
||||||
|
/// Always fallback to `()` (aka "always spontaneous decay")
|
||||||
|
FallbackToUnit,
|
||||||
|
/// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
|
||||||
|
FallbackToNiko,
|
||||||
|
/// Don't fallback at all
|
||||||
|
NoFallback,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> FnCtxt<'_, 'tcx> {
|
impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||||
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
|
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
|
||||||
@ -64,7 +75,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let diverging_fallback = self.calculate_diverging_fallback(&unresolved_variables);
|
let diverging_behavior = self.diverging_fallback_behavior();
|
||||||
|
let diverging_fallback =
|
||||||
|
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.
|
||||||
@ -78,6 +91,31 @@ 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::no_fallback => DivergingFallbackBehavior::NoFallback,
|
||||||
|
_ => {
|
||||||
|
self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, 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();
|
||||||
|
|
||||||
@ -232,6 +270,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||||||
fn calculate_diverging_fallback(
|
fn calculate_diverging_fallback(
|
||||||
&self,
|
&self,
|
||||||
unresolved_variables: &[Ty<'tcx>],
|
unresolved_variables: &[Ty<'tcx>],
|
||||||
|
behavior: DivergingFallbackBehavior,
|
||||||
) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
|
) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
|
||||||
debug!("calculate_diverging_fallback({:?})", unresolved_variables);
|
debug!("calculate_diverging_fallback({:?})", unresolved_variables);
|
||||||
|
|
||||||
@ -345,39 +384,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||||||
output: infer_var_infos.items().any(|info| info.output),
|
output: infer_var_infos.items().any(|info| info.output),
|
||||||
};
|
};
|
||||||
|
|
||||||
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
|
use DivergingFallbackBehavior::*;
|
||||||
// This case falls back to () to ensure that the code pattern in
|
match behavior {
|
||||||
// tests/ui/never_type/fallback-closure-ret.rs continues to
|
FallbackToUnit => {
|
||||||
// compile when never_type_fallback is enabled.
|
debug!("fallback to () - legacy: {:?}", diverging_vid);
|
||||||
//
|
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
||||||
// This rule is not readily explainable from first principles,
|
}
|
||||||
// but is rather intended as a patchwork fix to ensure code
|
FallbackToNiko => {
|
||||||
// which compiles before the stabilization of never type
|
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
|
||||||
// fallback continues to work.
|
// This case falls back to () to ensure that the code pattern in
|
||||||
//
|
// tests/ui/never_type/fallback-closure-ret.rs continues to
|
||||||
// Typically this pattern is encountered in a function taking a
|
// compile when never_type_fallback is enabled.
|
||||||
// closure as a parameter, where the return type of that closure
|
//
|
||||||
// (checked by `relationship.output`) is expected to implement
|
// This rule is not readily explainable from first principles,
|
||||||
// some trait (checked by `relationship.self_in_trait`). This
|
// but is rather intended as a patchwork fix to ensure code
|
||||||
// can come up in non-closure cases too, so we do not limit this
|
// which compiles before the stabilization of never type
|
||||||
// rule to specifically `FnOnce`.
|
// fallback continues to work.
|
||||||
//
|
//
|
||||||
// When the closure's body is something like `panic!()`, the
|
// Typically this pattern is encountered in a function taking a
|
||||||
// return type would normally be inferred to `!`. However, it
|
// closure as a parameter, where the return type of that closure
|
||||||
// needs to fall back to `()` in order to still compile, as the
|
// (checked by `relationship.output`) is expected to implement
|
||||||
// trait is specifically implemented for `()` but not `!`.
|
// some trait (checked by `relationship.self_in_trait`). This
|
||||||
//
|
// can come up in non-closure cases too, so we do not limit this
|
||||||
// For details on the requirements for these relationships to be
|
// rule to specifically `FnOnce`.
|
||||||
// set, see the relationship finding module in
|
//
|
||||||
// compiler/rustc_trait_selection/src/traits/relationships.rs.
|
// When the closure's body is something like `panic!()`, the
|
||||||
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
|
// return type would normally be inferred to `!`. However, it
|
||||||
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
// needs to fall back to `()` in order to still compile, as the
|
||||||
} else if can_reach_non_diverging {
|
// trait is specifically implemented for `()` but not `!`.
|
||||||
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
|
//
|
||||||
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
// For details on the requirements for these relationships to be
|
||||||
} else {
|
// set, see the relationship finding module in
|
||||||
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
|
// compiler/rustc_trait_selection/src/traits/relationships.rs.
|
||||||
diverging_fallback.insert(diverging_ty, Ty::new_diverging_default(self.tcx));
|
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
|
||||||
|
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
||||||
|
} else if can_reach_non_diverging {
|
||||||
|
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
|
||||||
|
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
||||||
|
} else {
|
||||||
|
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
|
||||||
|
diverging_fallback.insert(diverging_ty, self.tcx.types.never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NoFallback => {
|
||||||
|
debug!("no fallback - `rustc_never_type_mode = "no_fallback"`: {:?}", diverging_vid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,6 +815,8 @@ symbols! {
|
|||||||
fadd_algebraic,
|
fadd_algebraic,
|
||||||
fadd_fast,
|
fadd_fast,
|
||||||
fake_variadic,
|
fake_variadic,
|
||||||
|
fallback_to_niko,
|
||||||
|
fallback_to_unit,
|
||||||
fdiv_algebraic,
|
fdiv_algebraic,
|
||||||
fdiv_fast,
|
fdiv_fast,
|
||||||
feature,
|
feature,
|
||||||
@ -1233,6 +1235,7 @@ 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,
|
||||||
@ -1551,6 +1554,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_no_mir_inline,
|
rustc_no_mir_inline,
|
||||||
rustc_nonnull_optimization_guaranteed,
|
rustc_nonnull_optimization_guaranteed,
|
||||||
rustc_nounwind,
|
rustc_nounwind,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user