Skip to content

Commit 5792141

Browse files
committed
Implement subtype checking when right-hand side is a generic callable
This ended up amounting to just removing a check! Fixes #1236. But something is still not right when using type variables constrained to a list of values; added a failing test for that case.
1 parent 27b7ce0 commit 5792141

File tree

2 files changed

+114
-3
lines changed

2 files changed

+114
-3
lines changed

mypy/subtypes.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,18 @@ def is_callable_subtype(left: CallableType, right: CallableType,
202202
# Non-type cannot be a subtype of type.
203203
if right.is_type_obj() and not left.is_type_obj():
204204
return False
205-
if right.variables:
206-
# Subtyping is not currently supported for generic function as the supertype.
207-
return False
205+
206+
# A callable L is a subtype of a generic callable R if L is a
207+
# subtype of every type obtained from R by substituting types for
208+
# the variables of R. We can check this by simply leaving the
209+
# generic variables of R as type variables, effectively varying
210+
# over all possible values.
211+
212+
# It's okay even if these variables share ids with generic
213+
# type variables of L, because generating and solving
214+
# constraints for the variables of L to make L a subtype of R
215+
# (below) treats type variables on the two sides as independent.
216+
208217
if left.variables:
209218
# Apply generic type variables away in left via type inference.
210219
left = unify_generic_callable(left, right, ignore_return=ignore_return)

mypy/test/data/check-generics.test

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,81 @@ a = '' # E: Incompatible types in assignment (expression has type "str", variabl
640640
-- -----------------------------------------
641641

642642

643+
[case testSubtypingWithGenericFunctions]
644+
from typing import TypeVar
645+
A = TypeVar('A')
646+
B = TypeVar('B')
647+
648+
def f1(x: A) -> A: ...
649+
def f2(x: A) -> B: ...
650+
def f3(x: B) -> B: ...
651+
def f4(x: int) -> A: ...
652+
653+
y1 = f1
654+
y1 = f1
655+
y1 = f2
656+
y1 = f3
657+
y1 = f4 # E: Incompatible types in assignment (expression has type Callable[[int], A], variable has type Callable[[A], A])
658+
659+
y2 = f2
660+
y2 = f2
661+
y2 = f1 # E: Incompatible types in assignment (expression has type Callable[[A], A], variable has type Callable[[A], B])
662+
y2 = f3 # E: Incompatible types in assignment (expression has type Callable[[B], B], variable has type Callable[[A], B])
663+
y2 = f4 # E: Incompatible types in assignment (expression has type Callable[[int], A], variable has type Callable[[A], B])
664+
665+
y3 = f3
666+
y3 = f3
667+
y3 = f1
668+
y3 = f2
669+
y3 = f4 # E: Incompatible types in assignment (expression has type Callable[[int], A], variable has type Callable[[B], B])
670+
671+
y4 = f4
672+
y4 = f4
673+
y4 = f1 # E: Incompatible types in assignment (expression has type Callable[[A], A], variable has type Callable[[int], A])
674+
y4 = f2
675+
y4 = f3 # E: Incompatible types in assignment (expression has type Callable[[B], B], variable has type Callable[[int], A])
676+
677+
[case testSubtypingWithGenericInnerFunctions]
678+
from typing import TypeVar
679+
A = TypeVar('A')
680+
B = TypeVar('B')
681+
T = TypeVar('T')
682+
def outer(t: T) -> None:
683+
def f1(x: A) -> A: ...
684+
def f2(x: A) -> B: ...
685+
def f3(x: T) -> A: ...
686+
def f4(x: A) -> T: ...
687+
def f5(x: T) -> T: ...
688+
689+
y1 = f1
690+
y1 = f2
691+
y1 = f3 # E: Incompatible types in assignment (expression has type Callable[[T], A], variable has type Callable[[A], A])
692+
y1 = f4 # E: Incompatible types in assignment (expression has type Callable[[A], T], variable has type Callable[[A], A])
693+
y1 = f5 # E: Incompatible types in assignment (expression has type Callable[[T], T], variable has type Callable[[A], A])
694+
695+
y2 = f2
696+
y2 = f1 # E: Incompatible types in assignment (expression has type Callable[[A], A], variable has type Callable[[A], B])
697+
698+
y3 = f3
699+
y3 = f1 # E: Incompatible types in assignment (expression has type Callable[[A], A], variable has type Callable[[T], A])
700+
y3 = f2
701+
y3 = f4 # E: Incompatible types in assignment (expression has type Callable[[A], T], variable has type Callable[[T], A])
702+
y3 = f5 # E: Incompatible types in assignment (expression has type Callable[[T], T], variable has type Callable[[T], A])
703+
704+
y4 = f4
705+
y4 = f1 # E: Incompatible types in assignment (expression has type Callable[[A], A], variable has type Callable[[A], T])
706+
y4 = f2
707+
y4 = f3 # E: Incompatible types in assignment (expression has type Callable[[T], A], variable has type Callable[[A], T])
708+
y4 = f5 # E: Incompatible types in assignment (expression has type Callable[[T], T], variable has type Callable[[A], T])
709+
710+
y5 = f5
711+
y5 = f1
712+
y5 = f2
713+
y5 = f3
714+
y5 = f4
715+
[out]
716+
main: note: In function "outer":
717+
643718
[case testSubtypingWithGenericFunctionUsingTypevarWithValues]
644719
from typing import TypeVar, Callable
645720
T = TypeVar('T', int, str)
@@ -652,6 +727,13 @@ def g3(f: Callable[[object], object]) -> None: pass
652727
g3(f) # E: Argument 1 to "g3" has incompatible type Callable[[T], T]; \
653728
expected Callable[[object], object]
654729

730+
[case testSubtypingWithGenericFunctionUsingTypevarWithValues2-skip]
731+
from typing import TypeVar, Callable
732+
T = TypeVar('T', int, str)
733+
def f(x: T) -> T: pass
734+
g = f
735+
g = f
736+
655737

656738
--Operations on type variable types
657739
-- ---------------------------------
@@ -729,8 +811,28 @@ def identity(f: Callable[[A], None]) -> Callable[[A], None]:
729811
return f
730812
identity(voidify)(3)
731813

814+
[case testIdentityHigherOrderFunction3]
815+
from typing import Callable, TypeVar
816+
A = TypeVar('A')
817+
B = TypeVar('B')
818+
def fn(n: B) -> None: pass
819+
def identity(f: A) -> A:
820+
return f
821+
identity(fn)
822+
732823
[case testTypeVariableUnionAndCallableInTypeInference]
733824
from typing import Union, Callable, TypeVar
734825
T = TypeVar('T')
735826
def f(x: T, y: Union[T, Callable[[T], None]]) -> None: pass
736827
f('', '')
828+
829+
[case testGenericFunctionsWithUnalignedIds]
830+
from typing import TypeVar
831+
A = TypeVar('A')
832+
B = TypeVar('B')
833+
def f1(x: int, y: A) -> A: ...
834+
def f2(x: int, y: A) -> B: ...
835+
def f3(x: A, y: B) -> B: ...
836+
g = f1
837+
g = f2
838+
g = f3

0 commit comments

Comments
 (0)