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