Skip to content

Commit faeac0a

Browse files
rwbartongvanrossum
authored andcommitted
Take type variable value restrictions into account in overloading (#1406)
Addresses part of #1241. We could do something more precise (see discussion on that issue) but this should address most common cases.
1 parent 49e3864 commit faeac0a

File tree

4 files changed

+47
-4
lines changed

4 files changed

+47
-4
lines changed

mypy/checkexpr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,9 +1644,9 @@ def overload_arg_similarity(actual: Type, formal: Type) -> int:
16441644
# parameters, so no need to handle type variables occuring within
16451645
# a type.
16461646
if isinstance(actual, TypeVarType):
1647-
actual = actual.upper_bound
1647+
actual = actual.erase_to_union_or_bound()
16481648
if isinstance(formal, TypeVarType):
1649-
formal = formal.upper_bound
1649+
formal = formal.erase_to_union_or_bound()
16501650
if (isinstance(actual, NoneTyp) or isinstance(actual, AnyType) or
16511651
isinstance(formal, AnyType) or isinstance(formal, CallableType) or
16521652
(isinstance(actual, Instance) and actual.type.fallback_to_any)):

mypy/meet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ class C(A, B): ...
6666
# Since we are effectively working with the erased types, we only
6767
# need to handle occurrences of TypeVarType at the top level.
6868
if isinstance(t, TypeVarType):
69-
t = t.upper_bound
69+
t = t.erase_to_union_or_bound()
7070
if isinstance(s, TypeVarType):
71-
s = s.upper_bound
71+
s = s.erase_to_union_or_bound()
7272
if isinstance(t, Instance):
7373
if isinstance(s, Instance):
7474
# Only consider two classes non-disjoint if one is included in the mro

mypy/test/data/check-overloading.test

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,43 @@ def f(x: Sequence[int]) -> int: pass
631631
[out]
632632
main:4: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
633633

634+
[case testOverlapWithTypeVarsWithValues]
635+
from typing import overload, TypeVar
636+
AnyStr = TypeVar('AnyStr', bytes, str)
637+
638+
@overload
639+
def f(x: int) -> int: pass
640+
@overload
641+
def f(x: AnyStr) -> str: pass
642+
643+
f(1)() # E: "int" not callable
644+
f('1')() # E: "str" not callable
645+
f(b'1')() # E: "str" not callable
646+
f(1.0) # E: No overload variant of "f" matches argument types [builtins.float]
647+
648+
@overload
649+
def g(x: AnyStr, *a: AnyStr) -> None: pass
650+
@overload
651+
def g(x: int, *a: AnyStr) -> None: pass
652+
653+
g('foo')
654+
g('foo', 'bar')
655+
g('foo', b'bar') # E: Type argument 1 of "g" has incompatible value "object"
656+
g(1)
657+
g(1, 'foo')
658+
g(1, 'foo', b'bar') # E: Type argument 1 of "g" has incompatible value "object"
659+
[builtins fixtures/primitives.py]
660+
661+
[case testBadOverlapWithTypeVarsWithValues]
662+
from typing import overload, TypeVar
663+
AnyStr = TypeVar('AnyStr', bytes, str)
664+
665+
@overload
666+
def f(x: AnyStr) -> None: pass # E: Overloaded function signatures 1 and 2 overlap with incompatible return types
667+
@overload
668+
def f(x: str) -> bool: pass
669+
[builtins fixtures/primitives.py]
670+
634671
[case testOverlappingOverloadCounting]
635672
from typing import overload
636673
class A: pass

mypy/types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ def __init__(self, name: str, id: int, values: List[Type], upper_bound: Type,
339339
def accept(self, visitor: 'TypeVisitor[T]') -> T:
340340
return visitor.visit_type_var(self)
341341

342+
def erase_to_union_or_bound(self) -> Type:
343+
if self.values:
344+
return UnionType.make_simplified_union(self.values)
345+
else:
346+
return self.upper_bound
347+
342348
def serialize(self) -> JsonDict:
343349
return {'.class': 'TypeVarType',
344350
'name': self.name,

0 commit comments

Comments
 (0)