@@ -96,6 +96,7 @@ def __init__(
9696 is_self : bool = False ,
9797 rvalue : Expression | None = None ,
9898 suppress_errors : bool = False ,
99+ preserve_type_var_ids : bool = False ,
99100 ) -> None :
100101 self .is_lvalue = is_lvalue
101102 self .is_super = is_super
@@ -112,6 +113,10 @@ def __init__(
112113 assert is_lvalue
113114 self .rvalue = rvalue
114115 self .suppress_errors = suppress_errors
116+ # This attribute is only used to preserve old protocol member access logic.
117+ # It is needed to avoid infinite recursion in cases involving self-referential
118+ # generic methods, see find_member() for details. Do not use for other purposes!
119+ self .preserve_type_var_ids = preserve_type_var_ids
115120
116121 def named_type (self , name : str ) -> Instance :
117122 return self .chk .named_type (name )
@@ -142,6 +147,7 @@ def copy_modified(
142147 no_deferral = self .no_deferral ,
143148 rvalue = self .rvalue ,
144149 suppress_errors = self .suppress_errors ,
150+ preserve_type_var_ids = self .preserve_type_var_ids ,
145151 )
146152 if self_type is not None :
147153 mx .self_type = self_type
@@ -231,8 +237,6 @@ def analyze_member_access(
231237def _analyze_member_access (
232238 name : str , typ : Type , mx : MemberContext , override_info : TypeInfo | None = None
233239) -> Type :
234- # TODO: This and following functions share some logic with subtypes.find_member;
235- # consider refactoring.
236240 typ = get_proper_type (typ )
237241 if isinstance (typ , Instance ):
238242 return analyze_instance_member_access (name , typ , mx , override_info )
@@ -355,7 +359,8 @@ def analyze_instance_member_access(
355359 return AnyType (TypeOfAny .special_form )
356360 assert isinstance (method .type , Overloaded )
357361 signature = method .type
358- signature = freshen_all_functions_type_vars (signature )
362+ if not mx .preserve_type_var_ids :
363+ signature = freshen_all_functions_type_vars (signature )
359364 if not method .is_static :
360365 signature = check_self_arg (
361366 signature , mx .self_type , method .is_class , mx .context , name , mx .msg
@@ -928,7 +933,8 @@ def analyze_var(
928933def expand_without_binding (
929934 typ : Type , var : Var , itype : Instance , original_itype : Instance , mx : MemberContext
930935) -> Type :
931- typ = freshen_all_functions_type_vars (typ )
936+ if not mx .preserve_type_var_ids :
937+ typ = freshen_all_functions_type_vars (typ )
932938 typ = expand_self_type_if_needed (typ , mx , var , original_itype )
933939 expanded = expand_type_by_instance (typ , itype )
934940 freeze_all_type_vars (expanded )
@@ -938,7 +944,8 @@ def expand_without_binding(
938944def expand_and_bind_callable (
939945 functype : FunctionLike , var : Var , itype : Instance , name : str , mx : MemberContext
940946) -> Type :
941- functype = freshen_all_functions_type_vars (functype )
947+ if not mx .preserve_type_var_ids :
948+ functype = freshen_all_functions_type_vars (functype )
942949 typ = get_proper_type (expand_self_type (var , functype , mx .original_type ))
943950 assert isinstance (typ , FunctionLike )
944951 typ = check_self_arg (typ , mx .self_type , var .is_classmethod , mx .context , name , mx .msg )
@@ -1033,10 +1040,12 @@ def f(self: S) -> T: ...
10331040 return functype
10341041 else :
10351042 selfarg = get_proper_type (item .arg_types [0 ])
1036- # This level of erasure matches the one in checker.check_func_def (),
1037- # better keep these two checks consistent.
1038- if subtypes .is_subtype (
1043+ # This matches similar special-casing in bind_self (), see more details there.
1044+ self_callable = name == "__call__" and isinstance ( selfarg , CallableType )
1045+ if self_callable or subtypes .is_subtype (
10391046 dispatched_arg_type ,
1047+ # This level of erasure matches the one in checker.check_func_def(),
1048+ # better keep these two checks consistent.
10401049 erase_typevars (erase_to_bound (selfarg )),
10411050 # This is to work around the fact that erased ParamSpec and TypeVarTuple
10421051 # callables are not always compatible with non-erased ones both ways.
@@ -1197,15 +1206,10 @@ def analyze_class_attribute_access(
11971206 is_classmethod = (is_decorated and cast (Decorator , node .node ).func .is_class ) or (
11981207 isinstance (node .node , SYMBOL_FUNCBASE_TYPES ) and node .node .is_class
11991208 )
1200- is_staticmethod = (is_decorated and cast (Decorator , node .node ).func .is_static ) or (
1201- isinstance (node .node , SYMBOL_FUNCBASE_TYPES ) and node .node .is_static
1202- )
12031209 t = get_proper_type (t )
12041210 if isinstance (t , FunctionLike ) and is_classmethod :
12051211 t = check_self_arg (t , mx .self_type , False , mx .context , name , mx .msg )
1206- result = add_class_tvars (
1207- t , isuper , is_classmethod , is_staticmethod , mx .self_type , original_vars = original_vars
1208- )
1212+ result = add_class_tvars (t , isuper , is_classmethod , mx , original_vars = original_vars )
12091213 # __set__ is not called on class objects.
12101214 if not mx .is_lvalue :
12111215 result = analyze_descriptor_access (result , mx )
@@ -1337,8 +1341,7 @@ def add_class_tvars(
13371341 t : ProperType ,
13381342 isuper : Instance | None ,
13391343 is_classmethod : bool ,
1340- is_staticmethod : bool ,
1341- original_type : Type ,
1344+ mx : MemberContext ,
13421345 original_vars : Sequence [TypeVarLikeType ] | None = None ,
13431346) -> Type :
13441347 """Instantiate type variables during analyze_class_attribute_access,
@@ -1356,9 +1359,6 @@ class B(A[str]): pass
13561359 isuper: Current instance mapped to the superclass where method was defined, this
13571360 is usually done by map_instance_to_supertype()
13581361 is_classmethod: True if this method is decorated with @classmethod
1359- is_staticmethod: True if this method is decorated with @staticmethod
1360- original_type: The value of the type B in the expression B.foo() or the corresponding
1361- component in case of a union (this is used to bind the self-types)
13621362 original_vars: Type variables of the class callable on which the method was accessed
13631363 Returns:
13641364 Expanded method type with added type variables (when needed).
@@ -1379,11 +1379,11 @@ class B(A[str]): pass
13791379 # (i.e. appear in the return type of the class object on which the method was accessed).
13801380 if isinstance (t , CallableType ):
13811381 tvars = original_vars if original_vars is not None else []
1382- t = freshen_all_functions_type_vars (t )
1382+ if not mx .preserve_type_var_ids :
1383+ t = freshen_all_functions_type_vars (t )
13831384 if is_classmethod :
1384- t = bind_self (t , original_type , is_classmethod = True )
1385- if is_classmethod or is_staticmethod :
1386- assert isuper is not None
1385+ t = bind_self (t , mx .self_type , is_classmethod = True )
1386+ if isuper is not None :
13871387 t = expand_type_by_instance (t , isuper )
13881388 freeze_all_type_vars (t )
13891389 return t .copy_modified (variables = list (tvars ) + list (t .variables ))
@@ -1392,14 +1392,7 @@ class B(A[str]): pass
13921392 [
13931393 cast (
13941394 CallableType ,
1395- add_class_tvars (
1396- item ,
1397- isuper ,
1398- is_classmethod ,
1399- is_staticmethod ,
1400- original_type ,
1401- original_vars = original_vars ,
1402- ),
1395+ add_class_tvars (item , isuper , is_classmethod , mx , original_vars = original_vars ),
14031396 )
14041397 for item in t .items
14051398 ]
0 commit comments