@@ -4,8 +4,22 @@ use rustc_data_structures::{
4
4
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
5
5
unord::{UnordBag, UnordMap, UnordSet},
6
6
};
7
+ use rustc_hir::def_id::CRATE_DEF_ID;
7
8
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
8
9
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
+ /// Always fallback to `!` (which should be equivalent to never falling back + not making
18
+ /// never-to-any coercions unless necessary)
19
+ FallbackToNever,
20
+ /// Don't fallback at all
21
+ NoFallback,
22
+ }
9
23
10
24
impl<'tcx> FnCtxt<'_, 'tcx> {
11
25
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
@@ -64,7 +78,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
64
78
return false;
65
79
}
66
80
67
- let diverging_fallback = self.calculate_diverging_fallback(&unresolved_variables);
81
+ let diverging_behavior = self.diverging_fallback_behavior();
82
+ let diverging_fallback =
83
+ self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior);
68
84
69
85
// We do fallback in two passes, to try to generate
70
86
// better error messages.
@@ -78,6 +94,32 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
78
94
fallback_occurred
79
95
}
80
96
97
+ fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
98
+ let Some((mode, span)) = self
99
+ .tcx
100
+ .get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode)
101
+ .map(|attr| (attr.value_str().unwrap(), attr.span))
102
+ else {
103
+ if self.tcx.features().never_type_fallback {
104
+ return DivergingFallbackBehavior::FallbackToNiko;
105
+ }
106
+
107
+ return DivergingFallbackBehavior::FallbackToUnit;
108
+ };
109
+
110
+ match mode {
111
+ sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit,
112
+ sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko,
113
+ sym::fallback_to_never => DivergingFallbackBehavior::FallbackToNever,
114
+ sym::no_fallback => DivergingFallbackBehavior::NoFallback,
115
+ _ => {
116
+ 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`)"));
117
+
118
+ DivergingFallbackBehavior::FallbackToUnit
119
+ }
120
+ }
121
+ }
122
+
81
123
fn fallback_effects(&self) -> bool {
82
124
let unsolved_effects = self.unsolved_effects();
83
125
@@ -232,6 +274,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232
274
fn calculate_diverging_fallback(
233
275
&self,
234
276
unresolved_variables: &[Ty<'tcx>],
277
+ behavior: DivergingFallbackBehavior,
235
278
) -> UnordMap<Ty<'tcx>, Ty<'tcx>> {
236
279
debug!("calculate_diverging_fallback({:?})", unresolved_variables);
237
280
@@ -345,39 +388,61 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345
388
output: infer_var_infos.items().any(|info| info.output),
346
389
};
347
390
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));
391
+ use DivergingFallbackBehavior::*;
392
+ match behavior {
393
+ FallbackToUnit => {
394
+ debug!("fallback to () - legacy: {:?}", diverging_vid);
395
+ diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
396
+ }
397
+ FallbackToNiko => {
398
+ if found_infer_var_info.self_in_trait && found_infer_var_info.output {
399
+ // This case falls back to () to ensure that the code pattern in
400
+ // tests/ui/never_type/fallback-closure-ret.rs continues to
401
+ // compile when never_type_fallback is enabled.
402
+ //
403
+ // This rule is not readily explainable from first principles,
404
+ // but is rather intended as a patchwork fix to ensure code
405
+ // which compiles before the stabilization of never type
406
+ // fallback continues to work.
407
+ //
408
+ // Typically this pattern is encountered in a function taking a
409
+ // closure as a parameter, where the return type of that closure
410
+ // (checked by `relationship.output`) is expected to implement
411
+ // some trait (checked by `relationship.self_in_trait`). This
412
+ // can come up in non-closure cases too, so we do not limit this
413
+ // rule to specifically `FnOnce`.
414
+ //
415
+ // When the closure's body is something like `panic!()`, the
416
+ // return type would normally be inferred to `!`. However, it
417
+ // needs to fall back to `()` in order to still compile, as the
418
+ // trait is specifically implemented for `()` but not `!`.
419
+ //
420
+ // For details on the requirements for these relationships to be
421
+ // set, see the relationship finding module in
422
+ // compiler/rustc_trait_selection/src/traits/relationships.rs.
423
+ debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
424
+ diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
425
+ } else if can_reach_non_diverging {
426
+ debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
427
+ diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
428
+ } else {
429
+ debug!("fallback to ! - all diverging: {:?}", diverging_vid);
430
+ diverging_fallback.insert(diverging_ty, self.tcx.types.never);
431
+ }
432
+ }
433
+ FallbackToNever => {
434
+ debug!(
435
+ "fallback to ! - `rustc_never_type_mode = \"fallback_to_never\")`: {:?}",
436
+ diverging_vid
437
+ );
438
+ diverging_fallback.insert(diverging_ty, self.tcx.types.never);
439
+ }
440
+ NoFallback => {
441
+ debug!(
442
+ "no fallback - `rustc_never_type_mode = \"no_fallback\"`: {:?}",
443
+ diverging_vid
444
+ );
445
+ }
381
446
}
382
447
}
383
448
0 commit comments