From 5f7d66d33a08578cddb94529997383a30dfc66cb Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 11 Nov 2019 14:52:56 +0000 Subject: [PATCH 1/4] Tweak constraint inference against unions to exclude more unsatisfiable items --- mypy/constraints.py | 3 +++ test-data/unit/check-inference.test | 36 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/mypy/constraints.py b/mypy/constraints.py index a078eb0b08b5..e20330f88884 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -172,6 +172,9 @@ def infer_constraints_if_possible(template: Type, actual: Type, if (direction == SUPERTYPE_OF and not mypy.subtypes.is_subtype(actual, erase_typevars(template))): return None + if (direction == SUPERTYPE_OF and isinstance(template, TypeVarType) and + not mypy.subtypes.is_subtype(actual, erase_typevars(template.upper_bound))): + return None return infer_constraints(template, actual, direction) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 4dcbf411a779..c8dab735a1ab 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2761,3 +2761,39 @@ def f() -> None: class C: def __init__(self, a: int) -> None: self.a = a + +[case testUnionGenericWithBoundedVariable] +from typing import Generic, TypeVar, Union + +T = TypeVar('T', bound=A) +class Z(Generic[T]): + def __init__(self, y: T) -> None: + self.y = y + +class A: ... +class B(A): ... +F = TypeVar('F', bound=A) + +def q1(x: Union[F, Z[F]]) -> F: + if isinstance(x, Z): + return x.y + else: + return x + +def q2(x: Union[Z[F], F]) -> F: + if isinstance(x, Z): + return x.y + else: + return x + +b: B +reveal_type(q1(z)) +reveal_type(q2(z)) + +z: Z[B] +reveal_type(q1(z)) +reveal_type(q2(z)) + +reveal_type(q1(Z(b))) +reveal_type(q2(Z(b))) +[builtins fixtures/isinstancelist.pyi] From 05f38d11523ea827d517865da3f5552554974d3b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 11 Nov 2019 15:23:45 +0000 Subject: [PATCH 2/4] Update test and add a comment --- mypy/constraints.py | 1 + test-data/unit/check-inference.test | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index e20330f88884..b77b43ba7c12 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -174,6 +174,7 @@ def infer_constraints_if_possible(template: Type, actual: Type, return None if (direction == SUPERTYPE_OF and isinstance(template, TypeVarType) and not mypy.subtypes.is_subtype(actual, erase_typevars(template.upper_bound))): + # This is not caught by the above branch because of the erase_typevars() call. return None return infer_constraints(template, actual, direction) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index c8dab735a1ab..8c6674ea4066 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2787,13 +2787,13 @@ def q2(x: Union[Z[F], F]) -> F: return x b: B -reveal_type(q1(z)) -reveal_type(q2(z)) +reveal_type(q1(z)) # N: Revealed type is '__main__.B*' +reveal_type(q2(z)) # N: Revealed type is '__main__.B*' z: Z[B] -reveal_type(q1(z)) -reveal_type(q2(z)) +reveal_type(q1(z)) # N: Revealed type is '__main__.B*' +reveal_type(q2(z)) # N: Revealed type is '__main__.B*' -reveal_type(q1(Z(b))) -reveal_type(q2(Z(b))) +reveal_type(q1(Z(b))) # N: Revealed type is '__main__.B*' +reveal_type(q2(Z(b))) # N: Revealed type is '__main__.B*' [builtins fixtures/isinstancelist.pyi] From 2000e09553d467eb46c574e1579153b57b73ee9f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 11 Nov 2019 15:40:33 +0000 Subject: [PATCH 3/4] Fix typo in test --- test-data/unit/check-inference.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 8c6674ea4066..22b1029ac805 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2787,8 +2787,8 @@ def q2(x: Union[Z[F], F]) -> F: return x b: B -reveal_type(q1(z)) # N: Revealed type is '__main__.B*' -reveal_type(q2(z)) # N: Revealed type is '__main__.B*' +reveal_type(q1(b)) # N: Revealed type is '__main__.B*' +reveal_type(q2(b)) # N: Revealed type is '__main__.B*' z: Z[B] reveal_type(q1(z)) # N: Revealed type is '__main__.B*' From 3b782d5ad6b7db5f7870a3128d663469359d3527 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 11 Nov 2019 21:11:59 +0000 Subject: [PATCH 4/4] Tweak comment --- mypy/constraints.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index b77b43ba7c12..91e602f87ef6 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -174,7 +174,8 @@ def infer_constraints_if_possible(template: Type, actual: Type, return None if (direction == SUPERTYPE_OF and isinstance(template, TypeVarType) and not mypy.subtypes.is_subtype(actual, erase_typevars(template.upper_bound))): - # This is not caught by the above branch because of the erase_typevars() call. + # This is not caught by the above branch because of the erase_typevars() call, + # that would return 'Any' for a type variable. return None return infer_constraints(template, actual, direction)