@@ -4,8 +4,22 @@ 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 ;
78use rustc_infer:: infer:: { DefineOpaqueTypes , InferOk } ;
89use 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+ }
923
1024impl < ' tcx > FnCtxt < ' _ , ' tcx > {
1125 /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
@@ -64,7 +78,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
6478 return false ;
6579 }
6680
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) ;
6884
6985 // We do fallback in two passes, to try to generate
7086 // better error messages.
@@ -78,6 +94,32 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
7894 fallback_occurred
7995 }
8096
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+
81123 fn fallback_effects ( & self ) -> bool {
82124 let unsolved_effects = self . unsolved_effects ( ) ;
83125
@@ -232,6 +274,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
232274 fn calculate_diverging_fallback (
233275 & self ,
234276 unresolved_variables : & [ Ty < ' tcx > ] ,
277+ behavior : DivergingFallbackBehavior ,
235278 ) -> UnordMap < Ty < ' tcx > , Ty < ' tcx > > {
236279 debug ! ( "calculate_diverging_fallback({:?})" , unresolved_variables) ;
237280
@@ -345,39 +388,61 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
345388 output : infer_var_infos. items ( ) . any ( |info| info. output ) ,
346389 } ;
347390
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+ }
381446 }
382447 }
383448
0 commit comments