11//! Type inhabitedness logic.
2- use std:: ops:: ControlFlow :: { self , Break , Continue } ;
2+ use std:: {
3+ collections:: HashMap ,
4+ ops:: ControlFlow :: { self , Break , Continue } ,
5+ } ;
36
47use chalk_ir:: {
58 visit:: { TypeSuperVisitable , TypeVisitable , TypeVisitor } ,
@@ -9,14 +12,16 @@ use hir_def::{
912 adt:: VariantData , attr:: Attrs , visibility:: Visibility , AdtId , EnumVariantId , HasModule , Lookup ,
1013 ModuleId , VariantId ,
1114} ;
15+ use rustc_hash:: FxHashMap ;
1216
1317use crate :: {
1418 consteval:: try_const_usize, db:: HirDatabase , Binders , Interner , Substitution , Ty , TyKind ,
1519} ;
1620
1721/// Checks whether a type is visibly uninhabited from a particular module.
1822pub ( 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 ( ) } ;
2025 let inhabitedness = ty. visit_with ( & mut uninhabited_from, DebruijnIndex :: INNERMOST ) ;
2126 inhabitedness == BREAK_VISIBLY_UNINHABITED
2227}
@@ -32,7 +37,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
3237 let vars_attrs = db. variants_attrs ( variant. parent ) ;
3338 let is_local = variant. parent . lookup ( db. upcast ( ) ) . container . krate ( ) == target_mod. krate ( ) ;
3439
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 ( ) } ;
3642 let inhabitedness = uninhabited_from. visit_variant (
3743 variant. into ( ) ,
3844 & enum_data. variants [ variant. local_id ] . variant_data ,
@@ -45,6 +51,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
4551
4652struct UninhabitedFrom < ' a > {
4753 target_mod : ModuleId ,
54+ recursive_ty : FxHashMap < Ty , ( ) > ,
55+ // guard for preventing stack overflow in non trivial non terminating types
56+ max_depth : usize ,
4857 db : & ' a dyn HirDatabase ,
4958}
5059
@@ -65,7 +74,14 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
6574 ty : & Ty ,
6675 outer_binder : DebruijnIndex ,
6776 ) -> 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 ) {
6985 TyKind :: Adt ( adt, subst) => self . visit_adt ( adt. 0 , subst) ,
7086 TyKind :: Never => BREAK_VISIBLY_UNINHABITED ,
7187 TyKind :: Tuple ( ..) => ty. super_visit_with ( self , outer_binder) ,
@@ -75,7 +91,10 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
7591 } ,
7692
7793 TyKind :: Ref ( ..) | _ => CONTINUE_OPAQUELY_INHABITED ,
78- }
94+ } ;
95+ self . recursive_ty . remove ( ty) ;
96+ self . max_depth += 1 ;
97+ r
7998 }
8099
81100 fn interner ( & self ) -> Interner {
0 commit comments