@@ -7,6 +7,15 @@ use rustc_data_structures::{
7
7
use rustc_infer:: infer:: { DefineOpaqueTypes , InferOk } ;
8
8
use rustc_middle:: ty:: { self , Ty } ;
9
9
10
+ enum DivergingFallbackBehavior {
11
+ /// Always fallback to `()` (aka "always spontaneous decay")
12
+ FallbackToUnit ,
13
+ /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
14
+ FallbackToNiko ,
15
+ /// Don't fallback at all
16
+ Nope ,
17
+ }
18
+
10
19
impl < ' tcx > FnCtxt < ' _ , ' tcx > {
11
20
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
12
21
/// if fallback has occurred.
@@ -64,7 +73,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
64
73
return false ;
65
74
}
66
75
67
- let diverging_fallback = self . calculate_diverging_fallback ( & unresolved_variables) ;
76
+ let diverging_behavior = self . diverging_fallback_behavior ( ) ;
77
+ let diverging_fallback =
78
+ self . calculate_diverging_fallback ( & unresolved_variables, diverging_behavior) ;
68
79
69
80
// We do fallback in two passes, to try to generate
70
81
// better error messages.
@@ -78,6 +89,26 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
78
89
fallback_occurred
79
90
}
80
91
92
+ fn diverging_fallback_behavior ( & self ) -> DivergingFallbackBehavior {
93
+ let niko = self . tcx . features ( ) . never_type_fallback ;
94
+ let nope = self . tcx . features ( ) . no_never_type_fallback ;
95
+
96
+ if niko as u32 + nope as u32 > 1 {
97
+ self . tcx . dcx ( ) . err ( "can't enable multiple never type fallback features (`never_type_fallback`, `no_never_type_fallback`)" ) ;
98
+ return DivergingFallbackBehavior :: FallbackToUnit ;
99
+ }
100
+
101
+ if niko {
102
+ return DivergingFallbackBehavior :: FallbackToNiko ;
103
+ }
104
+
105
+ if nope {
106
+ return DivergingFallbackBehavior :: Nope ;
107
+ }
108
+
109
+ DivergingFallbackBehavior :: FallbackToUnit
110
+ }
111
+
81
112
fn fallback_effects ( & self ) -> bool {
82
113
let unsolved_effects = self . unsolved_effects ( ) ;
83
114
@@ -232,6 +263,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232
263
fn calculate_diverging_fallback (
233
264
& self ,
234
265
unresolved_variables : & [ Ty < ' tcx > ] ,
266
+ behavior : DivergingFallbackBehavior ,
235
267
) -> UnordMap < Ty < ' tcx > , Ty < ' tcx > > {
236
268
debug ! ( "calculate_diverging_fallback({:?})" , unresolved_variables) ;
237
269
@@ -345,39 +377,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345
377
output : infer_var_infos. items ( ) . any ( |info| info. output ) ,
346
378
} ;
347
379
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 ) ) ;
380
+ use DivergingFallbackBehavior :: * ;
381
+ match behavior {
382
+ FallbackToUnit => {
383
+ debug ! ( "fallback to () - legacy: {:?}" , diverging_vid) ;
384
+ diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
385
+ }
386
+ FallbackToNiko => {
387
+ if found_infer_var_info. self_in_trait && found_infer_var_info. output {
388
+ // This case falls back to () to ensure that the code pattern in
389
+ // tests/ui/never_type/fallback-closure-ret.rs continues to
390
+ // compile when never_type_fallback is enabled.
391
+ //
392
+ // This rule is not readily explainable from first principles,
393
+ // but is rather intended as a patchwork fix to ensure code
394
+ // which compiles before the stabilization of never type
395
+ // fallback continues to work.
396
+ //
397
+ // Typically this pattern is encountered in a function taking a
398
+ // closure as a parameter, where the return type of that closure
399
+ // (checked by `relationship.output`) is expected to implement
400
+ // some trait (checked by `relationship.self_in_trait`). This
401
+ // can come up in non-closure cases too, so we do not limit this
402
+ // rule to specifically `FnOnce`.
403
+ //
404
+ // When the closure's body is something like `panic!()`, the
405
+ // return type would normally be inferred to `!`. However, it
406
+ // needs to fall back to `()` in order to still compile, as the
407
+ // trait is specifically implemented for `()` but not `!`.
408
+ //
409
+ // For details on the requirements for these relationships to be
410
+ // set, see the relationship finding module in
411
+ // compiler/rustc_trait_selection/src/traits/relationships.rs.
412
+ debug ! ( "fallback to () - found trait and projection: {:?}" , diverging_vid) ;
413
+ diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
414
+ } else if can_reach_non_diverging {
415
+ debug ! ( "fallback to () - reached non-diverging: {:?}" , diverging_vid) ;
416
+ diverging_fallback. insert ( diverging_ty, self . tcx . types . unit ) ;
417
+ } else {
418
+ debug ! ( "fallback to ! - all diverging: {:?}" , diverging_vid) ;
419
+ diverging_fallback. insert ( diverging_ty, self . tcx . types . never ) ;
420
+ }
421
+ }
422
+ Nope => {
423
+ debug ! ( "not fallback - `feature(no_never_type_fallback)`: {:?}" , diverging_vid) ;
424
+ }
381
425
}
382
426
}
383
427
0 commit comments