@@ -2823,7 +2823,7 @@ def function_type(self, func: FuncBase) -> FunctionLike:
28232823 return function_type (func , self .named_type ('builtins.function' ))
28242824
28252825 def find_isinstance_check (self , n : Expression ) -> 'Tuple[TypeMap, TypeMap]' :
2826- return find_isinstance_check (n , self .type_map )
2826+ return find_isinstance_check (n , self .type_map , self )
28272827
28282828 def push_type_map (self , type_map : 'TypeMap' ) -> None :
28292829 if type_map is None :
@@ -2897,7 +2897,45 @@ def conditional_type_map(expr: Expression,
28972897 return {}, {}
28982898
28992899
2900- def partition_by_callable (type : Type ) -> Tuple [List [Type ], List [Type ]]:
2900+ def intersect_instance_callable (type : Instance , callable_type : CallableType ) -> Type :
2901+ """Creates a fake type that represents the intersection of an
2902+ Instance and a CallableType.
2903+
2904+ It operates by creating a bare-minimum dummy TypeInfo that
2905+ subclasses type and adds a __call__ method matching callable_type."""
2906+
2907+ # Build the fake ClassDef and TypeInfo together.
2908+ # The ClassDef is full of lies and doesn't actually contain a body.
2909+ cdef = ClassDef ("<callable subtype of {}>" .format (type .type .name ()), Block ([]))
2910+ info = TypeInfo (SymbolTable (), cdef , '<dummy>' )
2911+ cdef .info = info
2912+ info .bases = [type ]
2913+ info .calculate_mro ()
2914+
2915+ # Build up a fake FuncDef so we can populate the symbol table.
2916+ func_def = FuncDef ('__call__' , [], Block ([]), callable_type )
2917+ func_def .info = info
2918+ info .names ['__call__' ] = SymbolTableNode (MDEF , func_def , callable_type )
2919+
2920+ return Instance (info , [])
2921+
2922+
2923+ def make_fake_callable (type : Instance , typechecker : TypeChecker ) -> Type :
2924+ """Produce a new type that makes type Callable with a generic callable type."""
2925+
2926+ fallback = typechecker .named_type ('builtins.function' )
2927+ callable_type = CallableType ([AnyType (TypeOfAny .explicit ),
2928+ AnyType (TypeOfAny .explicit )],
2929+ [nodes .ARG_STAR , nodes .ARG_STAR2 ],
2930+ [None , None ],
2931+ ret_type = AnyType (TypeOfAny .explicit ),
2932+ fallback = fallback ,
2933+ is_ellipsis_args = True )
2934+
2935+ return intersect_instance_callable (type , callable_type )
2936+
2937+
2938+ def partition_by_callable (type : Type , typechecker : TypeChecker ) -> Tuple [List [Type ], List [Type ]]:
29012939 """Takes in a type and partitions that type into callable subtypes and
29022940 uncallable subtypes.
29032941
@@ -2918,29 +2956,43 @@ def partition_by_callable(type: Type) -> Tuple[List[Type], List[Type]]:
29182956 callables = []
29192957 uncallables = []
29202958 for subtype in type .relevant_items ():
2921- subcallables , subuncallables = partition_by_callable (subtype )
2959+ subcallables , subuncallables = partition_by_callable (subtype , typechecker )
29222960 callables .extend (subcallables )
29232961 uncallables .extend (subuncallables )
29242962 return callables , uncallables
29252963
29262964 if isinstance (type , TypeVarType ):
2927- return partition_by_callable (type .erase_to_union_or_bound ())
2965+ # We could do better probably?
2966+ # Refine the the type variable's bound as our type in the case that
2967+ # callable() is true. This unfortuantely loses the information that
2968+ # the type is a type variable in that branch.
2969+ # This matches what is done for isinstance, but it may be possible to
2970+ # do better.
2971+ # If it is possible for the false branch to execute, return the original
2972+ # type to avoid losing type information.
2973+ callables , uncallables = partition_by_callable (type .erase_to_union_or_bound (), typechecker )
2974+ uncallables = [type ] if len (uncallables ) else []
2975+ return callables , uncallables
29282976
29292977 if isinstance (type , Instance ):
29302978 method = type .type .get_method ('__call__' )
29312979 if method and method .type :
2932- callables , uncallables = partition_by_callable (method .type )
2980+ callables , uncallables = partition_by_callable (method .type , typechecker )
29332981 if len (callables ) and not len (uncallables ):
29342982 # Only consider the type callable if its __call__ method is
29352983 # definitely callable.
29362984 return [type ], []
2937- return [], [type ]
29382985
2939- return [], [type ]
2986+ ret = make_fake_callable (type , typechecker )
2987+ return [ret ], [type ]
2988+
2989+ # We don't know how properly make the type callable.
2990+ return [type ], [type ]
29402991
29412992
29422993def conditional_callable_type_map (expr : Expression ,
29432994 current_type : Optional [Type ],
2995+ typechecker : TypeChecker ,
29442996 ) -> Tuple [TypeMap , TypeMap ]:
29452997 """Takes in an expression and the current type of the expression.
29462998
@@ -2954,7 +3006,7 @@ def conditional_callable_type_map(expr: Expression,
29543006 if isinstance (current_type , AnyType ):
29553007 return {}, {}
29563008
2957- callables , uncallables = partition_by_callable (current_type )
3009+ callables , uncallables = partition_by_callable (current_type , typechecker )
29583010
29593011 if len (callables ) and len (uncallables ):
29603012 callable_map = {expr : UnionType .make_union (callables )} if len (callables ) else None
@@ -3083,6 +3135,7 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap:
30833135
30843136def find_isinstance_check (node : Expression ,
30853137 type_map : Dict [Expression , Type ],
3138+ typechecker : TypeChecker ,
30863139 ) -> Tuple [TypeMap , TypeMap ]:
30873140 """Find any isinstance checks (within a chain of ands). Includes
30883141 implicit and explicit checks for None and calls to callable.
@@ -3136,7 +3189,7 @@ def find_isinstance_check(node: Expression,
31363189 expr = node .args [0 ]
31373190 if literal (expr ) == LITERAL_TYPE :
31383191 vartype = type_map [expr ]
3139- return conditional_callable_type_map (expr , vartype )
3192+ return conditional_callable_type_map (expr , vartype , typechecker )
31403193 elif isinstance (node , ComparisonExpr ) and experiments .STRICT_OPTIONAL :
31413194 # Check for `x is None` and `x is not None`.
31423195 is_not = node .operators == ['is not' ]
@@ -3196,23 +3249,23 @@ def find_isinstance_check(node: Expression,
31963249 else_map = {ref : else_type } if not isinstance (else_type , UninhabitedType ) else None
31973250 return if_map , else_map
31983251 elif isinstance (node , OpExpr ) and node .op == 'and' :
3199- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3200- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3252+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3253+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
32013254
32023255 # (e1 and e2) is true if both e1 and e2 are true,
32033256 # and false if at least one of e1 and e2 is false.
32043257 return (and_conditional_maps (left_if_vars , right_if_vars ),
32053258 or_conditional_maps (left_else_vars , right_else_vars ))
32063259 elif isinstance (node , OpExpr ) and node .op == 'or' :
3207- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3208- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3260+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3261+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
32093262
32103263 # (e1 or e2) is true if at least one of e1 or e2 is true,
32113264 # and false if both e1 and e2 are false.
32123265 return (or_conditional_maps (left_if_vars , right_if_vars ),
32133266 and_conditional_maps (left_else_vars , right_else_vars ))
32143267 elif isinstance (node , UnaryExpr ) and node .op == 'not' :
3215- left , right = find_isinstance_check (node .expr , type_map )
3268+ left , right = find_isinstance_check (node .expr , type_map , typechecker )
32163269 return right , left
32173270
32183271 # Not a supported isinstance check
0 commit comments