11use std:: { cell:: RefCell , collections:: HashSet , rc:: Rc } ;
22
3- use crate :: { InferFailReason , LuaTypeDeclId } ;
3+ use crate :: { InferFailReason , LuaType , LuaTypeDeclId } ;
44
55pub type InferGuardRef = Rc < InferGuard > ;
66
77/// Guard to prevent infinite recursion with optimized lazy allocation
88///
99/// This guard uses a lazy allocation strategy:
1010/// - Fork is zero-cost (no HashSet allocation)
11- /// - `current` HashSet is only created when needed (write-on-create)
11+ /// - Visited declaration and instantiated type sets are only created when needed
1212/// - Most child guards never allocate memory if they only read from parents
1313///
1414/// # Memory Layout
@@ -23,9 +23,10 @@ pub type InferGuardRef = Rc<InferGuard>;
2323/// ```
2424#[ derive( Debug , Clone ) ]
2525pub struct InferGuard {
26- /// Current level's visited types (lazily allocated)
27- /// Only created when we need to add a new type not in parent chain
26+ /// Current level's visited type declarations (lazily allocated)
2827 current : RefCell < Option < HashSet < LuaTypeDeclId > > > ,
28+ /// Current level's visited instantiated types (lazily allocated)
29+ current_types : RefCell < Option < HashSet < LuaType > > > ,
2930 /// Parent guard (shared reference)
3031 parent : Option < Rc < InferGuard > > ,
3132}
@@ -34,6 +35,7 @@ impl InferGuard {
3435 pub fn new ( ) -> Rc < Self > {
3536 Rc :: new ( Self {
3637 current : RefCell :: new ( None ) ,
38+ current_types : RefCell :: new ( None ) ,
3739 parent : None ,
3840 } )
3941 }
@@ -44,6 +46,7 @@ impl InferGuard {
4446 pub fn fork ( self : & Rc < Self > ) -> Rc < Self > {
4547 Rc :: new ( Self {
4648 current : RefCell :: new ( None ) , // Lazy allocation
49+ current_types : RefCell :: new ( None ) ,
4750 parent : Some ( Rc :: clone ( self ) ) ,
4851 } )
4952 }
@@ -70,6 +73,20 @@ impl InferGuard {
7073 Ok ( ( ) )
7174 }
7275
76+ pub fn check_type ( & self , typ : & LuaType ) -> Result < ( ) , InferFailReason > {
77+ if self . contains_type_in_parents ( typ) {
78+ return Err ( InferFailReason :: RecursiveInfer ) ;
79+ }
80+
81+ let mut current_types = self . current_types . borrow_mut ( ) ;
82+ let current_types = current_types. get_or_insert_with ( HashSet :: default) ;
83+ if !current_types. insert ( typ. clone ( ) ) {
84+ return Err ( InferFailReason :: RecursiveInfer ) ;
85+ }
86+
87+ Ok ( ( ) )
88+ }
89+
7390 /// Check if a type has been visited in parent chain
7491 fn contains_in_parents ( & self , type_id : & LuaTypeDeclId ) -> bool {
7592 let mut current_parent = self . parent . as_ref ( ) ;
@@ -84,6 +101,19 @@ impl InferGuard {
84101 false
85102 }
86103
104+ fn contains_type_in_parents ( & self , typ : & LuaType ) -> bool {
105+ let mut current_parent = self . parent . as_ref ( ) ;
106+ while let Some ( parent) = current_parent {
107+ if let Some ( ref types) = * parent. current_types . borrow ( )
108+ && types. contains ( typ)
109+ {
110+ return true ;
111+ }
112+ current_parent = parent. parent . as_ref ( ) ;
113+ }
114+ false
115+ }
116+
87117 /// Check if a type has been visited (without modifying the guard)
88118 pub fn contains ( & self , type_id : & LuaTypeDeclId ) -> bool {
89119 // Check current level
@@ -99,6 +129,11 @@ impl InferGuard {
99129 /// Get the depth of current level
100130 pub fn current_depth ( & self ) -> usize {
101131 self . current . borrow ( ) . as_ref ( ) . map_or ( 0 , |set| set. len ( ) )
132+ + self
133+ . current_types
134+ . borrow ( )
135+ . as_ref ( )
136+ . map_or ( 0 , |set| set. len ( ) )
102137 }
103138
104139 /// Get the total depth of the entire guard chain
@@ -127,7 +162,7 @@ impl InferGuard {
127162 /// Useful for debugging and performance analysis
128163 #[ cfg( test) ]
129164 pub fn has_allocated ( & self ) -> bool {
130- self . current . borrow ( ) . is_some ( )
165+ self . current . borrow ( ) . is_some ( ) || self . current_types . borrow ( ) . is_some ( )
131166 }
132167}
133168
0 commit comments