@@ -7,6 +7,15 @@ use rustc_data_structures::{
77use rustc_infer:: infer:: { DefineOpaqueTypes , InferOk } ;
88use rustc_middle:: ty:: { self , Ty } ;
99
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+
1019impl < ' tcx > FnCtxt < ' _ , ' tcx > {
1120 /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
1221 /// if fallback has occurred.
@@ -64,7 +73,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
6473 return false ;
6574 }
6675
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) ;
6879
6980 // We do fallback in two passes, to try to generate
7081 // better error messages.
@@ -78,6 +89,26 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
7889 fallback_occurred
7990 }
8091
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+
81112 fn fallback_effects ( & self ) -> bool {
82113 let unsolved_effects = self . unsolved_effects ( ) ;
83114
@@ -232,6 +263,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232263 fn calculate_diverging_fallback (
233264 & self ,
234265 unresolved_variables : & [ Ty < ' tcx > ] ,
266+ behavior : DivergingFallbackBehavior ,
235267 ) -> UnordMap < Ty < ' tcx > , Ty < ' tcx > > {
236268 debug ! ( "calculate_diverging_fallback({:?})" , unresolved_variables) ;
237269
@@ -345,39 +377,51 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345377 output : infer_var_infos. items ( ) . any ( |info| info. output ) ,
346378 } ;
347379
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+ }
381425 }
382426 }
383427
0 commit comments