Skip to content

Commit 16adf9f

Browse files
committed
Fix type inference problem with overloaded functions
Fixes #496.
1 parent b644f6c commit 16adf9f

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

mypy/constraints.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mypy.expandtype import expand_caller_var_args
1010
from mypy.maptype import map_instance_to_supertype
1111
from mypy import nodes
12+
import mypy.subtypes
1213

1314

1415
SUBTYPE_OF = 0 # type: int
@@ -234,7 +235,7 @@ def infer_against_overloaded(self, overloaded: Overloaded,
234235
# the first overload item, and by only matching the return type. This
235236
# seems to work somewhat well, but we should really use a more
236237
# reliable technique.
237-
item = overloaded.items()[0]
238+
item = find_matching_overload_item(overloaded, template)
238239
return infer_constraints(template.ret_type, item.ret_type,
239240
self.direction)
240241

@@ -282,3 +283,16 @@ def neg_op(op: int) -> int:
282283
return SUBTYPE_OF
283284
else:
284285
raise ValueError('Invalid operator {}'.format(op))
286+
287+
288+
def find_matching_overload_item(overloaded: Overloaded, template: Callable) -> Callable:
289+
"""Disambiguate overload item against a template."""
290+
items = overloaded.items()
291+
for item in items:
292+
# Return type may be indeterminate in the template, so ignore it when performing a
293+
# subtype check.
294+
if mypy.subtypes.is_callable_subtype(item, template, ignore_return=True):
295+
return item
296+
# Fall back to the first item if we can't find a match. This is totally arbitrary --
297+
# maybe we should just bail out at this point.
298+
return items[0]

mypy/subtypes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
160160
return False
161161

162162

163-
def is_callable_subtype(left: Callable, right: Callable) -> bool:
163+
def is_callable_subtype(left: Callable, right: Callable, ignore_return: bool = False) -> bool:
164164
"""Is left a subtype of right?"""
165165
# TODO: Support named arguments, **args, etc.
166166
# Non-type cannot be a subtype of type.
@@ -176,7 +176,7 @@ def is_callable_subtype(left: Callable, right: Callable) -> bool:
176176
return False
177177

178178
# Check return types.
179-
if not is_subtype(left.ret_type, right.ret_type):
179+
if not ignore_return and not is_subtype(left.ret_type, right.ret_type):
180180
return False
181181

182182
# Check argument types.

mypy/test/data/check-typevar-values.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,25 @@ from typing import typevar, Generic
463463
T = typevar('T', values=(int, str))
464464
class C(Generic[T]):
465465
def f(self, x: int = None) -> None: pass
466+
467+
[case testTypevarValuesWithOverloadedFunctionSpecialCase]
468+
from typing import typevar, overload, Function
469+
470+
T = typevar('T', values=(int, str))
471+
def f(x: T) -> None:
472+
y = m(g, x)
473+
x = y
474+
y = object()
475+
476+
A = typevar('A')
477+
R = typevar('R')
478+
def m(f: Function[[A], R], it: A) -> A: pass
479+
480+
@overload
481+
def g(x: int) -> int: return x
482+
@overload
483+
def g(x: str) -> str: return x
484+
[out]
485+
main: In function "f":
486+
main, line 7: Incompatible types in assignment (expression has type "object", variable has type "int")
487+
main, line 7: Incompatible types in assignment (expression has type "object", variable has type "str")

0 commit comments

Comments
 (0)