@@ -2809,7 +2809,7 @@ def function_type(self, func: FuncBase) -> FunctionLike:
28092809 return function_type (func , self .named_type ('builtins.function' ))
28102810
28112811 def find_isinstance_check (self , n : Expression ) -> 'Tuple[TypeMap, TypeMap]' :
2812- return find_isinstance_check (n , self .type_map )
2812+ return find_isinstance_check (n , self .type_map , self )
28132813
28142814 def push_type_map (self , type_map : 'TypeMap' ) -> None :
28152815 if type_map is None :
@@ -2883,7 +2883,45 @@ def conditional_type_map(expr: Expression,
28832883 return {}, {}
28842884
28852885
2886- def partition_by_callable (type : Type ) -> Tuple [List [Type ], List [Type ]]:
2886+ def intersect_instance_callable (type : Instance , callable_type : CallableType ) -> Type :
2887+ """Creates a fake type that represents the intersection of an
2888+ Instance and a CallableType.
2889+
2890+ It operates by creating a bare-minimum dummy TypeInfo that
2891+ subclasses type and adds a __call__ method matching callable_type."""
2892+
2893+ # Build the fake ClassDef and TypeInfo together.
2894+ # The ClassDef is full of lies and doesn't actually contain a body.
2895+ cdef = ClassDef ("<callable subtype of {}>" .format (type .type .name ()), Block ([]))
2896+ info = TypeInfo (SymbolTable (), cdef , '<dummy>' )
2897+ cdef .info = info
2898+ info .bases = [type ]
2899+ info .calculate_mro ()
2900+
2901+ # Build up a fake FuncDef so we can populate the symbol table.
2902+ func_def = FuncDef ('__call__' , [], Block ([]), callable_type )
2903+ func_def .info = info
2904+ info .names ['__call__' ] = SymbolTableNode (MDEF , func_def , callable_type )
2905+
2906+ return Instance (info , [])
2907+
2908+
2909+ def make_fake_callable (type : Instance , typechecker : TypeChecker ) -> Type :
2910+ """Produce a new type that makes type Callable with a generic callable type."""
2911+
2912+ fallback = typechecker .named_type ('builtins.function' )
2913+ callable_type = CallableType ([AnyType (TypeOfAny .explicit ),
2914+ AnyType (TypeOfAny .explicit )],
2915+ [nodes .ARG_STAR , nodes .ARG_STAR2 ],
2916+ [None , None ],
2917+ ret_type = AnyType (TypeOfAny .explicit ),
2918+ fallback = fallback ,
2919+ is_ellipsis_args = True )
2920+
2921+ return intersect_instance_callable (type , callable_type )
2922+
2923+
2924+ def partition_by_callable (type : Type , typechecker : TypeChecker ) -> Tuple [List [Type ], List [Type ]]:
28872925 """Takes in a type and partitions that type into callable subtypes and
28882926 uncallable subtypes.
28892927
@@ -2904,29 +2942,43 @@ def partition_by_callable(type: Type) -> Tuple[List[Type], List[Type]]:
29042942 callables = []
29052943 uncallables = []
29062944 for subtype in type .relevant_items ():
2907- subcallables , subuncallables = partition_by_callable (subtype )
2945+ subcallables , subuncallables = partition_by_callable (subtype , typechecker )
29082946 callables .extend (subcallables )
29092947 uncallables .extend (subuncallables )
29102948 return callables , uncallables
29112949
29122950 if isinstance (type , TypeVarType ):
2913- return partition_by_callable (type .erase_to_union_or_bound ())
2951+ # We could do better probably?
2952+ # Refine the the type variable's bound as our type in the case that
2953+ # callable() is true. This unfortuantely loses the information that
2954+ # the type is a type variable in that branch.
2955+ # This matches what is done for isinstance, but it may be possible to
2956+ # do better.
2957+ # If it is possible for the false branch to execute, return the original
2958+ # type to avoid losing type information.
2959+ callables , uncallables = partition_by_callable (type .erase_to_union_or_bound (), typechecker )
2960+ uncallables = [type ] if len (uncallables ) else []
2961+ return callables , uncallables
29142962
29152963 if isinstance (type , Instance ):
29162964 method = type .type .get_method ('__call__' )
29172965 if method and method .type :
2918- callables , uncallables = partition_by_callable (method .type )
2966+ callables , uncallables = partition_by_callable (method .type , typechecker )
29192967 if len (callables ) and not len (uncallables ):
29202968 # Only consider the type callable if its __call__ method is
29212969 # definitely callable.
29222970 return [type ], []
2923- return [], [type ]
29242971
2925- return [], [type ]
2972+ ret = make_fake_callable (type , typechecker )
2973+ return [ret ], [type ]
2974+
2975+ # We don't know how properly make the type callable.
2976+ return [type ], [type ]
29262977
29272978
29282979def conditional_callable_type_map (expr : Expression ,
29292980 current_type : Optional [Type ],
2981+ typechecker : TypeChecker ,
29302982 ) -> Tuple [TypeMap , TypeMap ]:
29312983 """Takes in an expression and the current type of the expression.
29322984
@@ -2940,7 +2992,7 @@ def conditional_callable_type_map(expr: Expression,
29402992 if isinstance (current_type , AnyType ):
29412993 return {}, {}
29422994
2943- callables , uncallables = partition_by_callable (current_type )
2995+ callables , uncallables = partition_by_callable (current_type , typechecker )
29442996
29452997 if len (callables ) and len (uncallables ):
29462998 callable_map = {expr : UnionType .make_union (callables )} if len (callables ) else None
@@ -3069,6 +3121,7 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap:
30693121
30703122def find_isinstance_check (node : Expression ,
30713123 type_map : Dict [Expression , Type ],
3124+ typechecker : TypeChecker ,
30723125 ) -> Tuple [TypeMap , TypeMap ]:
30733126 """Find any isinstance checks (within a chain of ands). Includes
30743127 implicit and explicit checks for None and calls to callable.
@@ -3122,7 +3175,7 @@ def find_isinstance_check(node: Expression,
31223175 expr = node .args [0 ]
31233176 if literal (expr ) == LITERAL_TYPE :
31243177 vartype = type_map [expr ]
3125- return conditional_callable_type_map (expr , vartype )
3178+ return conditional_callable_type_map (expr , vartype , typechecker )
31263179 elif isinstance (node , ComparisonExpr ) and experiments .STRICT_OPTIONAL :
31273180 # Check for `x is None` and `x is not None`.
31283181 is_not = node .operators == ['is not' ]
@@ -3182,23 +3235,23 @@ def find_isinstance_check(node: Expression,
31823235 else_map = {ref : else_type } if not isinstance (else_type , UninhabitedType ) else None
31833236 return if_map , else_map
31843237 elif isinstance (node , OpExpr ) and node .op == 'and' :
3185- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3186- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3238+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3239+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
31873240
31883241 # (e1 and e2) is true if both e1 and e2 are true,
31893242 # and false if at least one of e1 and e2 is false.
31903243 return (and_conditional_maps (left_if_vars , right_if_vars ),
31913244 or_conditional_maps (left_else_vars , right_else_vars ))
31923245 elif isinstance (node , OpExpr ) and node .op == 'or' :
3193- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3194- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3246+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3247+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
31953248
31963249 # (e1 or e2) is true if at least one of e1 or e2 is true,
31973250 # and false if both e1 and e2 are false.
31983251 return (or_conditional_maps (left_if_vars , right_if_vars ),
31993252 and_conditional_maps (left_else_vars , right_else_vars ))
32003253 elif isinstance (node , UnaryExpr ) and node .op == 'not' :
3201- left , right = find_isinstance_check (node .expr , type_map )
3254+ left , right = find_isinstance_check (node .expr , type_map , typechecker )
32023255 return right , left
32033256
32043257 # Not a supported isinstance check
0 commit comments