@@ -4,8 +4,19 @@ 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
+ /// Don't fallback at all
18
+ NoFallback ,
19
+ }
9
20
10
21
impl < ' tcx > FnCtxt < ' _ , ' tcx > {
11
22
/// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
@@ -64,7 +75,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
64
75
return false ;
65
76
}
66
77
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) ;
68
81
69
82
// We do fallback in two passes, to try to generate
70
83
// better error messages.
@@ -78,6 +91,31 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
78
91
fallback_occurred
79
92
}
80
93
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
+
81
119
fn fallback_effects ( & self ) -> bool {
82
120
let unsolved_effects = self . unsolved_effects ( ) ;
83
121
@@ -232,6 +270,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232
270
fn calculate_diverging_fallback (
233
271
& self ,
234
272
unresolved_variables : & [ Ty < ' tcx > ] ,
273
+ behavior : DivergingFallbackBehavior ,
235
274
) -> UnordMap < Ty < ' tcx > , Ty < ' tcx > > {
236
275
debug ! ( "calculate_diverging_fallback({:?})" , unresolved_variables) ;
237
276
@@ -345,39 +384,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345
384
output : infer_var_infos. items ( ) . any ( |info| info. output ) ,
346
385
} ;
347
386
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
+ }
381
432
}
382
433
}
383
434
0 commit comments