@@ -69,7 +69,7 @@ use crate::types::tuple::{TupleSpec, TupleSpecBuilder};
6969pub ( crate ) use crate :: types:: typed_dict:: { TypedDictParams , TypedDictType , walk_typed_dict_type} ;
7070pub use crate :: types:: variance:: TypeVarVariance ;
7171use crate :: types:: variance:: VarianceInferable ;
72- use crate :: types:: visitor:: any_over_type;
72+ use crate :: types:: visitor:: { any_over_type, exceeds_max_specialization_depth } ;
7373use crate :: unpack:: EvaluationMode ;
7474use crate :: { Db , FxOrderSet , Module , Program } ;
7575pub ( crate ) use class:: { ClassLiteral , ClassType , GenericAlias , KnownClass } ;
@@ -827,10 +827,14 @@ impl<'db> Type<'db> {
827827 Self :: Dynamic ( DynamicType :: Unknown )
828828 }
829829
830- pub ( crate ) fn divergent ( scope : ScopeId < ' db > ) -> Self {
830+ pub ( crate ) fn divergent ( scope : Option < ScopeId < ' db > > ) -> Self {
831831 Self :: Dynamic ( DynamicType :: Divergent ( DivergentType { scope } ) )
832832 }
833833
834+ pub ( crate ) const fn is_divergent ( & self ) -> bool {
835+ matches ! ( self , Type :: Dynamic ( DynamicType :: Divergent ( _) ) )
836+ }
837+
834838 pub const fn is_unknown ( & self ) -> bool {
835839 matches ! ( self , Type :: Dynamic ( DynamicType :: Unknown ) )
836840 }
@@ -6652,7 +6656,7 @@ impl<'db> Type<'db> {
66526656 match self {
66536657 Type :: TypeVar ( bound_typevar) => match type_mapping {
66546658 TypeMapping :: Specialization ( specialization) => {
6655- specialization. get ( db, bound_typevar) . unwrap_or ( self )
6659+ specialization. get ( db, bound_typevar) . unwrap_or ( self ) . fallback_to_divergent ( db )
66566660 }
66576661 TypeMapping :: PartialSpecialization ( partial) => {
66586662 partial. get ( db, bound_typevar) . unwrap_or ( self )
@@ -7214,6 +7218,16 @@ impl<'db> Type<'db> {
72147218 pub ( super ) fn has_divergent_type ( self , db : & ' db dyn Db , div : Type < ' db > ) -> bool {
72157219 any_over_type ( db, self , & |ty| ty == div, false )
72167220 }
7221+
7222+ /// If the specialization depth of `self` exceeds the maximum limit allowed,
7223+ /// return `Divergent`. Otherwise, return `self`.
7224+ pub ( super ) fn fallback_to_divergent ( self , db : & ' db dyn Db ) -> Type < ' db > {
7225+ if exceeds_max_specialization_depth ( db, self ) {
7226+ Type :: divergent ( None )
7227+ } else {
7228+ self
7229+ }
7230+ }
72177231}
72187232
72197233impl < ' db > From < & Type < ' db > > for Type < ' db > {
@@ -7659,7 +7673,7 @@ impl<'db> KnownInstanceType<'db> {
76597673#[ derive( Copy , Clone , Debug , Eq , Hash , PartialEq , salsa:: Update , get_size2:: GetSize ) ]
76607674pub struct DivergentType < ' db > {
76617675 /// The scope where this divergence was detected.
7662- scope : ScopeId < ' db > ,
7676+ scope : Option < ScopeId < ' db > > ,
76637677}
76647678
76657679#[ derive( Copy , Clone , Debug , Eq , Hash , PartialEq , salsa:: Update , get_size2:: GetSize ) ]
@@ -11772,7 +11786,7 @@ pub(crate) mod tests {
1177211786 let file_scope_id = FileScopeId :: global ( ) ;
1177311787 let scope = file_scope_id. to_scope_id ( & db, file) ;
1177411788
11775- let div = Type :: Dynamic ( DynamicType :: Divergent ( DivergentType { scope } ) ) ;
11789+ let div = Type :: Dynamic ( DynamicType :: Divergent ( DivergentType { scope : Some ( scope ) } ) ) ;
1177611790
1177711791 // The `Divergent` type must not be eliminated in union with other dynamic types,
1177811792 // as this would prevent detection of divergent type inference using `Divergent`.
0 commit comments