Skip to content

Commit 19bc337

Browse files
committed
Add rustc_never_type_mode crate-level attribute to allow experimenting
1 parent ee03c28 commit 19bc337

File tree

3 files changed

+96
-34
lines changed

3 files changed

+96
-34
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+7
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
580580
"`may_dangle` has unstable semantics and may be removed in the future",
581581
),
582582

583+
rustc_attr!(
584+
rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|no_fallback"), ErrorFollowing,
585+
@only_local: true,
586+
"`rustc_never_type_fallback` is used to experiment with never type fallback and work on \
587+
never type stabilization, and will never be stable"
588+
),
589+
583590
// ==========================================================================
584591
// Internal attributes: Runtime related:
585592
// ==========================================================================

compiler/rustc_hir_typeck/src/fallback.rs

+85-34
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@ use rustc_data_structures::{
44
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
55
unord::{UnordBag, UnordMap, UnordSet},
66
};
7+
use rustc_hir::def_id::CRATE_DEF_ID;
78
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
89
use rustc_middle::ty::{self, Ty};
10+
use rustc_span::sym;
11+
12+
enum DivergingFallbackBehavior {
13+
/// Always fallback to `()` (aka "always spontaneous decay")
14+
FallbackToUnit,
15+
/// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
16+
FallbackToNiko,
17+
/// Don't fallback at all
18+
NoFallback,
19+
}
920

1021
impl<'tcx> FnCtxt<'_, 'tcx> {
1122
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
@@ -64,7 +75,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
6475
return false;
6576
}
6677

67-
let diverging_fallback = self.calculate_diverging_fallback(&unresolved_variables);
78+
let diverging_behavior = self.diverging_fallback_behavior();
79+
let diverging_fallback =
80+
self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior);
6881

6982
// We do fallback in two passes, to try to generate
7083
// better error messages.
@@ -78,6 +91,31 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
7891
fallback_occurred
7992
}
8093

94+
fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
95+
let Some((mode, span)) = self
96+
.tcx
97+
.get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode)
98+
.map(|attr| (attr.value_str().unwrap(), attr.span))
99+
else {
100+
if self.tcx.features().never_type_fallback {
101+
return DivergingFallbackBehavior::FallbackToNiko;
102+
}
103+
104+
return DivergingFallbackBehavior::FallbackToUnit;
105+
};
106+
107+
match mode {
108+
sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit,
109+
sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko,
110+
sym::no_fallback => DivergingFallbackBehavior::NoFallback,
111+
_ => {
112+
self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, and `no_fallback`)"));
113+
114+
DivergingFallbackBehavior::FallbackToUnit
115+
}
116+
}
117+
}
118+
81119
fn fallback_effects(&self) -> bool {
82120
let unsolved_effects = self.unsolved_effects();
83121

@@ -232,6 +270,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232270
fn calculate_diverging_fallback(
233271
&self,
234272
unresolved_variables: &[Ty<'tcx>],
273+
behavior: DivergingFallbackBehavior,
235274
) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
236275
debug!("calculate_diverging_fallback({:?})", unresolved_variables);
237276

@@ -345,39 +384,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345384
output: infer_var_infos.items().any(|info| info.output),
346385
};
347386

348-
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
349-
// This case falls back to () to ensure that the code pattern in
350-
// tests/ui/never_type/fallback-closure-ret.rs continues to
351-
// compile when never_type_fallback is enabled.
352-
//
353-
// This rule is not readily explainable from first principles,
354-
// but is rather intended as a patchwork fix to ensure code
355-
// which compiles before the stabilization of never type
356-
// fallback continues to work.
357-
//
358-
// Typically this pattern is encountered in a function taking a
359-
// closure as a parameter, where the return type of that closure
360-
// (checked by `relationship.output`) is expected to implement
361-
// some trait (checked by `relationship.self_in_trait`). This
362-
// can come up in non-closure cases too, so we do not limit this
363-
// rule to specifically `FnOnce`.
364-
//
365-
// When the closure's body is something like `panic!()`, the
366-
// return type would normally be inferred to `!`. However, it
367-
// needs to fall back to `()` in order to still compile, as the
368-
// trait is specifically implemented for `()` but not `!`.
369-
//
370-
// For details on the requirements for these relationships to be
371-
// set, see the relationship finding module in
372-
// compiler/rustc_trait_selection/src/traits/relationships.rs.
373-
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
374-
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
375-
} else if can_reach_non_diverging {
376-
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
377-
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
378-
} else {
379-
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
380-
diverging_fallback.insert(diverging_ty, Ty::new_diverging_default(self.tcx));
387+
use DivergingFallbackBehavior::*;
388+
match behavior {
389+
FallbackToUnit => {
390+
debug!("fallback to () - legacy: {:?}", diverging_vid);
391+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
392+
}
393+
FallbackToNiko => {
394+
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
395+
// This case falls back to () to ensure that the code pattern in
396+
// tests/ui/never_type/fallback-closure-ret.rs continues to
397+
// compile when never_type_fallback is enabled.
398+
//
399+
// This rule is not readily explainable from first principles,
400+
// but is rather intended as a patchwork fix to ensure code
401+
// which compiles before the stabilization of never type
402+
// fallback continues to work.
403+
//
404+
// Typically this pattern is encountered in a function taking a
405+
// closure as a parameter, where the return type of that closure
406+
// (checked by `relationship.output`) is expected to implement
407+
// some trait (checked by `relationship.self_in_trait`). This
408+
// can come up in non-closure cases too, so we do not limit this
409+
// rule to specifically `FnOnce`.
410+
//
411+
// When the closure's body is something like `panic!()`, the
412+
// return type would normally be inferred to `!`. However, it
413+
// needs to fall back to `()` in order to still compile, as the
414+
// trait is specifically implemented for `()` but not `!`.
415+
//
416+
// For details on the requirements for these relationships to be
417+
// set, see the relationship finding module in
418+
// compiler/rustc_trait_selection/src/traits/relationships.rs.
419+
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
420+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
421+
} else if can_reach_non_diverging {
422+
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
423+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
424+
} else {
425+
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
426+
diverging_fallback.insert(diverging_ty, self.tcx.types.never);
427+
}
428+
}
429+
NoFallback => {
430+
debug!("no fallback - `rustc_never_type_mode = "no_fallback"`: {:?}", diverging_vid);
431+
}
381432
}
382433
}
383434

compiler/rustc_span/src/symbol.rs

+4
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,8 @@ symbols! {
815815
fadd_algebraic,
816816
fadd_fast,
817817
fake_variadic,
818+
fallback_to_niko,
819+
fallback_to_unit,
818820
fdiv_algebraic,
819821
fdiv_fast,
820822
feature,
@@ -1233,6 +1235,7 @@ symbols! {
12331235
no_crate_inject,
12341236
no_debug,
12351237
no_default_passes,
1238+
no_fallback,
12361239
no_implicit_prelude,
12371240
no_inline,
12381241
no_link,
@@ -1551,6 +1554,7 @@ symbols! {
15511554
rustc_mir,
15521555
rustc_must_implement_one_of,
15531556
rustc_never_returns_null_ptr,
1557+
rustc_never_type_mode,
15541558
rustc_no_mir_inline,
15551559
rustc_nonnull_optimization_guaranteed,
15561560
rustc_nounwind,

0 commit comments

Comments
 (0)