From 08154ab70944284d7385020534c03fa03c9a519d Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:50:28 +0100 Subject: [PATCH 1/4] Fix inference with UninhabitedType --- mypy/join.py | 13 +++++++++---- test-data/unit/check-inference.test | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/mypy/join.py b/mypy/join.py index bf88f43d88fe..19c3dcb758cc 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -108,12 +108,17 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: # TODO: contravariant case should use meet but pass seen instances as # an argument to keep track of recursive checks. elif type_var.variance in (INVARIANT, CONTRAVARIANT): - if not is_equivalent(ta, sa): + if isinstance(ta, UninhabitedType): + new_type = sa + elif isinstance(sa, UninhabitedType): + new_type = ta + elif not is_equivalent(ta, sa): self.seen_instances.pop() return object_from_instance(t) - # If the types are different but equivalent, then an Any is involved - # so using a join in the contravariant case is also OK. - new_type = join_types(ta, sa, self) + else: + # If the types are different but equivalent, then an Any is involved + # so using a join in the contravariant case is also OK. + new_type = join_types(ta, sa, self) elif isinstance(type_var, TypeVarTupleType): new_type = get_proper_type(join_types(ta, sa, self)) # Put the joined arguments back into instance in the normal form: diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 953855e502d6..47dd6acddd83 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3813,3 +3813,18 @@ def m1() -> float: ... def m2() -> float: ... reveal_type(Combine(m1, m2)) # N: Revealed type is "builtins.float" [builtins fixtures/list.pyi] + +[case testInferenceWithUninhabitedType] +from typing import Dict, Generic, TypeVar + +T = TypeVar("T") + +class A(Generic[T]): ... + +def func1(a: A[T], b: T) -> T: ... +def func2(a: T, b: A[T]) -> T: ... + +def a1(a: A[Dict[str, int]]) -> None: + reveal_type(func1(a, {})) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + reveal_type(func2({}, a)) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" +[builtins fixtures/dict.pyi] From 8ec9fc5ec364ea05caea7ea0c418d931d3506552 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Wed, 6 Mar 2024 23:29:22 +0100 Subject: [PATCH 2/4] Use proper type --- mypy/join.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/join.py b/mypy/join.py index 19c3dcb758cc..c5ca87e94019 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -108,9 +108,9 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: # TODO: contravariant case should use meet but pass seen instances as # an argument to keep track of recursive checks. elif type_var.variance in (INVARIANT, CONTRAVARIANT): - if isinstance(ta, UninhabitedType): + if isinstance(ta_proper, UninhabitedType): new_type = sa - elif isinstance(sa, UninhabitedType): + elif isinstance(sa_proper, UninhabitedType): new_type = ta elif not is_equivalent(ta, sa): self.seen_instances.pop() From d9e93e058a009564774ae28c7c8e9a5505b57829 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 7 Mar 2024 00:46:06 +0100 Subject: [PATCH 3/4] Add additional test cases --- test-data/unit/check-inference.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 47dd6acddd83..47d44180dcd0 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3820,6 +3820,7 @@ from typing import Dict, Generic, TypeVar T = TypeVar("T") class A(Generic[T]): ... +class B(Dict[T, T]): ... def func1(a: A[T], b: T) -> T: ... def func2(a: T, b: A[T]) -> T: ... @@ -3827,4 +3828,15 @@ def func2(a: T, b: A[T]) -> T: ... def a1(a: A[Dict[str, int]]) -> None: reveal_type(func1(a, {})) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" reveal_type(func2({}, a)) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]" + +def a2(check: bool, a: B[str]) -> None: + reveal_type(a if check else {}) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]" + +def a3() -> None: + a = {} # E: Need type annotation for "a" (hint: "a: Dict[, ] = ...") + b = {1: {}} # E: Need type annotation for "b" + c = {1: {}, 2: {"key": {}}} # E: Need type annotation for "c" + reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]" + reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[Any, Any]]" + reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[builtins.str, builtins.dict[Any, Any]]]" [builtins fixtures/dict.pyi] From cd4600f5bf48db080307447a6eed2bce83da8b7a Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 7 Mar 2024 01:37:13 +0100 Subject: [PATCH 4/4] Deal with edge case --- mypy/join.py | 4 ++-- test-data/unit/check-inference.test | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mypy/join.py b/mypy/join.py index c5ca87e94019..3603e9fefb7a 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -108,9 +108,9 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType: # TODO: contravariant case should use meet but pass seen instances as # an argument to keep track of recursive checks. elif type_var.variance in (INVARIANT, CONTRAVARIANT): - if isinstance(ta_proper, UninhabitedType): + if isinstance(ta_proper, UninhabitedType) and not ta_proper.is_noreturn: new_type = sa - elif isinstance(sa_proper, UninhabitedType): + elif isinstance(sa_proper, UninhabitedType) and not sa_proper.is_noreturn: new_type = ta elif not is_equivalent(ta, sa): self.seen_instances.pop() diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 47d44180dcd0..1b1ce607bf28 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -3815,7 +3815,7 @@ reveal_type(Combine(m1, m2)) # N: Revealed type is "builtins.float" [builtins fixtures/list.pyi] [case testInferenceWithUninhabitedType] -from typing import Dict, Generic, TypeVar +from typing import Dict, Generic, List, Never, TypeVar T = TypeVar("T") @@ -3839,4 +3839,11 @@ def a3() -> None: reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]" reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[Any, Any]]" reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[builtins.str, builtins.dict[Any, Any]]]" + +def a4(x: List[str], y: List[Never]) -> None: + z1 = [x, y] + z2 = [y, x] + reveal_type(z1) # N: Revealed type is "builtins.list[builtins.object]" + reveal_type(z2) # N: Revealed type is "builtins.list[builtins.object]" + z1[1].append("asdf") # E: "object" has no attribute "append" [builtins fixtures/dict.pyi]