@@ -295,3 +295,91 @@ pub(super) fn any_over_type<'db>(
295295 visitor. visit_type ( db, ty) ;
296296 visitor. found_matching_type . get ( )
297297}
298+
299+ /// Returns the maximum number of layers of generic specializations for a given type.
300+ ///
301+ /// For example, `int` has a depth of `0`, `list[int]` has a depth of `1`, and `list[set[int]]`
302+ /// has a depth of `2`. A set-theoretic type like `list[int] | list[list[int]]` has a maximum
303+ /// depth of `2`.
304+ pub ( super ) fn specialization_depth ( db : & dyn Db , ty : Type < ' _ > ) -> usize {
305+ struct SpecializationDepthVisitor < ' db > {
306+ seen_types : RefCell < FxIndexSet < NonAtomicType < ' db > > > ,
307+ max_depth : Cell < usize > ,
308+ }
309+
310+ impl < ' db > TypeVisitor < ' db > for SpecializationDepthVisitor < ' db > {
311+ fn should_visit_lazy_type_attributes ( & self ) -> bool {
312+ false
313+ }
314+
315+ fn visit_type ( & self , db : & ' db dyn Db , ty : Type < ' db > ) {
316+ match TypeKind :: from ( ty) {
317+ TypeKind :: Atomic => {
318+ if ty. is_divergent ( ) {
319+ self . max_depth . set ( usize:: MAX ) ;
320+ }
321+ }
322+ TypeKind :: NonAtomic ( non_atomic_type) => {
323+ if !self . seen_types . borrow_mut ( ) . insert ( non_atomic_type) {
324+ return ;
325+ }
326+
327+ walk_non_atomic_type ( db, non_atomic_type, self ) ;
328+ let child_max_depth = self . max_depth . get ( ) ;
329+
330+ let self_depth: usize =
331+ matches ! ( non_atomic_type, NonAtomicType :: GenericAlias ( _) ) . into ( ) ;
332+
333+ self . max_depth . set (
334+ self . max_depth
335+ . get ( )
336+ . max ( child_max_depth. saturating_add ( self_depth) ) ,
337+ ) ;
338+ }
339+ }
340+ }
341+ }
342+
343+ let visitor = SpecializationDepthVisitor {
344+ seen_types : RefCell :: new ( FxIndexSet :: default ( ) ) ,
345+ max_depth : Cell :: new ( 0 ) ,
346+ } ;
347+ visitor. visit_type ( db, ty) ;
348+ visitor. max_depth . get ( )
349+ }
350+
351+ #[ cfg( test) ]
352+ mod tests {
353+ use super :: * ;
354+ use crate :: { db:: tests:: setup_db, types:: KnownClass } ;
355+
356+ #[ test]
357+ fn test_generics_layering_depth ( ) {
358+ let db = setup_db ( ) ;
359+
360+ let list_of_int =
361+ KnownClass :: List . to_specialized_instance ( & db, [ KnownClass :: Int . to_instance ( & db) ] ) ;
362+ assert_eq ! ( specialization_depth( & db, list_of_int) , 1 ) ;
363+
364+ let list_of_list_of_int = KnownClass :: List . to_specialized_instance ( & db, [ list_of_int] ) ;
365+ assert_eq ! ( specialization_depth( & db, list_of_list_of_int) , 2 ) ;
366+
367+ let list_of_list_of_list_of_int =
368+ KnownClass :: List . to_specialized_instance ( & db, [ list_of_list_of_int] ) ;
369+ assert_eq ! ( specialization_depth( & db, list_of_list_of_list_of_int) , 3 ) ;
370+
371+ let set_of_dict_of_str_and_list_of_int = KnownClass :: Set . to_specialized_instance (
372+ & db,
373+ [ KnownClass :: Dict
374+ . to_specialized_instance ( & db, [ KnownClass :: Str . to_instance ( & db) , list_of_int] ) ] ,
375+ ) ;
376+ assert_eq ! (
377+ specialization_depth( & db, set_of_dict_of_str_and_list_of_int) ,
378+ 3
379+ ) ;
380+
381+ let union_type =
382+ UnionType :: from_elements ( & db, [ list_of_list_of_int, list_of_list_of_list_of_int] ) ;
383+ assert_eq ! ( specialization_depth( & db, union_type) , 3 ) ;
384+ }
385+ }
0 commit comments