@@ -9,14 +9,16 @@ use hir_def::{
9
9
adt:: VariantData , attr:: Attrs , visibility:: Visibility , AdtId , EnumVariantId , HasModule , Lookup ,
10
10
ModuleId , VariantId ,
11
11
} ;
12
+ use rustc_hash:: FxHashSet ;
12
13
13
14
use crate :: {
14
15
consteval:: try_const_usize, db:: HirDatabase , Binders , Interner , Substitution , Ty , TyKind ,
15
16
} ;
16
17
17
18
/// Checks whether a type is visibly uninhabited from a particular module.
18
19
pub ( crate ) fn is_ty_uninhabited_from ( ty : & Ty , target_mod : ModuleId , db : & dyn HirDatabase ) -> bool {
19
- let mut uninhabited_from = UninhabitedFrom { target_mod, db } ;
20
+ let mut uninhabited_from =
21
+ UninhabitedFrom { target_mod, db, max_depth : 500 , recursive_ty : FxHashSet :: default ( ) } ;
20
22
let inhabitedness = ty. visit_with ( & mut uninhabited_from, DebruijnIndex :: INNERMOST ) ;
21
23
inhabitedness == BREAK_VISIBLY_UNINHABITED
22
24
}
@@ -32,7 +34,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
32
34
let vars_attrs = db. variants_attrs ( variant. parent ) ;
33
35
let is_local = variant. parent . lookup ( db. upcast ( ) ) . container . krate ( ) == target_mod. krate ( ) ;
34
36
35
- let mut uninhabited_from = UninhabitedFrom { target_mod, db } ;
37
+ let mut uninhabited_from =
38
+ UninhabitedFrom { target_mod, db, max_depth : 500 , recursive_ty : FxHashSet :: default ( ) } ;
36
39
let inhabitedness = uninhabited_from. visit_variant (
37
40
variant. into ( ) ,
38
41
& enum_data. variants [ variant. local_id ] . variant_data ,
@@ -45,6 +48,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
45
48
46
49
struct UninhabitedFrom < ' a > {
47
50
target_mod : ModuleId ,
51
+ recursive_ty : FxHashSet < Ty > ,
52
+ // guard for preventing stack overflow in non trivial non terminating types
53
+ max_depth : usize ,
48
54
db : & ' a dyn HirDatabase ,
49
55
}
50
56
@@ -65,7 +71,14 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
65
71
ty : & Ty ,
66
72
outer_binder : DebruijnIndex ,
67
73
) -> ControlFlow < VisiblyUninhabited > {
68
- match ty. kind ( Interner ) {
74
+ if self . recursive_ty . contains ( ty) || self . max_depth == 0 {
75
+ // rustc considers recursive types always inhabited. I think it is valid to consider
76
+ // recursive types as always uninhabited, but we should do what rustc is doing.
77
+ return CONTINUE_OPAQUELY_INHABITED ;
78
+ }
79
+ self . recursive_ty . insert ( ty. clone ( ) ) ;
80
+ self . max_depth -= 1 ;
81
+ let r = match ty. kind ( Interner ) {
69
82
TyKind :: Adt ( adt, subst) => self . visit_adt ( adt. 0 , subst) ,
70
83
TyKind :: Never => BREAK_VISIBLY_UNINHABITED ,
71
84
TyKind :: Tuple ( ..) => ty. super_visit_with ( self , outer_binder) ,
@@ -75,7 +88,10 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
75
88
} ,
76
89
77
90
TyKind :: Ref ( ..) | _ => CONTINUE_OPAQUELY_INHABITED ,
78
- }
91
+ } ;
92
+ self . recursive_ty . remove ( ty) ;
93
+ self . max_depth += 1 ;
94
+ r
79
95
}
80
96
81
97
fn interner ( & self ) -> Interner {
0 commit comments