Skip to content

Commit 80ebf84

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

File tree

3 files changed

+92
-34
lines changed

3 files changed

+92
-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

+81-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,27 @@ 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+
return DivergingFallbackBehavior::FallbackToUnit;
101+
};
102+
103+
match mode {
104+
sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit,
105+
sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko,
106+
sym::no_fallback => DivergingFallbackBehavior::NoFallback,
107+
_ => {
108+
self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, and `no_fallback`)"));
109+
110+
DivergingFallbackBehavior::FallbackToUnit
111+
}
112+
}
113+
}
114+
81115
fn fallback_effects(&self) -> bool {
82116
let unsolved_effects = self.unsolved_effects();
83117

@@ -232,6 +266,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232266
fn calculate_diverging_fallback(
233267
&self,
234268
unresolved_variables: &[Ty<'tcx>],
269+
behavior: DivergingFallbackBehavior,
235270
) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
236271
debug!("calculate_diverging_fallback({:?})", unresolved_variables);
237272

@@ -345,39 +380,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345380
output: infer_var_infos.items().any(|info| info.output),
346381
};
347382

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));
383+
use DivergingFallbackBehavior::*;
384+
match behavior {
385+
FallbackToUnit => {
386+
debug!("fallback to () - legacy: {:?}", diverging_vid);
387+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
388+
}
389+
FallbackToNiko => {
390+
if found_infer_var_info.self_in_trait && found_infer_var_info.output {
391+
// This case falls back to () to ensure that the code pattern in
392+
// tests/ui/never_type/fallback-closure-ret.rs continues to
393+
// compile when never_type_fallback is enabled.
394+
//
395+
// This rule is not readily explainable from first principles,
396+
// but is rather intended as a patchwork fix to ensure code
397+
// which compiles before the stabilization of never type
398+
// fallback continues to work.
399+
//
400+
// Typically this pattern is encountered in a function taking a
401+
// closure as a parameter, where the return type of that closure
402+
// (checked by `relationship.output`) is expected to implement
403+
// some trait (checked by `relationship.self_in_trait`). This
404+
// can come up in non-closure cases too, so we do not limit this
405+
// rule to specifically `FnOnce`.
406+
//
407+
// When the closure's body is something like `panic!()`, the
408+
// return type would normally be inferred to `!`. However, it
409+
// needs to fall back to `()` in order to still compile, as the
410+
// trait is specifically implemented for `()` but not `!`.
411+
//
412+
// For details on the requirements for these relationships to be
413+
// set, see the relationship finding module in
414+
// compiler/rustc_trait_selection/src/traits/relationships.rs.
415+
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
416+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
417+
} else if can_reach_non_diverging {
418+
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
419+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
420+
} else {
421+
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
422+
diverging_fallback.insert(diverging_ty, self.tcx.types.never);
423+
}
424+
}
425+
NoFallback => {
426+
debug!("no fallback - `rustc_never_type_mode = "no_fallback"`: {:?}", diverging_vid);
427+
}
381428
}
382429
}
383430

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)