Skip to content

Commit 74927d5

Browse files
authored
Include ambiguous into UninhabitedType identity (#19648)
Fixes #19641, but also reveals a test that was only passing by coincidence. This inference has never worked correctly: ```python from typing import Mapping, Never d: dict[Never, Never] def run() -> Mapping[str, int]: return d ``` As discussed, I updated the test to expect failure in that case. We should only special-case inline collection literals for that, everything else (including locals) can cause undesired false negatives.
1 parent 766c43c commit 74927d5

File tree

5 files changed

+34
-8
lines changed

5 files changed

+34
-8
lines changed

mypy/test/testsolve.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@ def test_multiple_variables(self) -> None:
6464
)
6565

6666
def test_no_constraints_for_var(self) -> None:
67-
self.assert_solve([self.fx.t], [], [self.fx.uninhabited])
68-
self.assert_solve([self.fx.t, self.fx.s], [], [self.fx.uninhabited, self.fx.uninhabited])
67+
self.assert_solve([self.fx.t], [], [self.fx.a_uninhabited])
68+
self.assert_solve(
69+
[self.fx.t, self.fx.s], [], [self.fx.a_uninhabited, self.fx.a_uninhabited]
70+
)
6971
self.assert_solve(
7072
[self.fx.t, self.fx.s],
7173
[self.supc(self.fx.s, self.fx.a)],
72-
[self.fx.uninhabited, self.fx.a],
74+
[self.fx.a_uninhabited, self.fx.a],
7375
)
7476

7577
def test_simple_constraints_with_dynamic_type(self) -> None:
@@ -116,7 +118,7 @@ def test_poly_no_constraints(self) -> None:
116118
self.assert_solve(
117119
[self.fx.t, self.fx.u],
118120
[],
119-
[self.fx.uninhabited, self.fx.uninhabited],
121+
[self.fx.a_uninhabited, self.fx.a_uninhabited],
120122
allow_polymorphic=True,
121123
)
122124

@@ -152,7 +154,7 @@ def test_poly_free_pair_with_bounds_uninhabited(self) -> None:
152154
self.assert_solve(
153155
[self.fx.ub, self.fx.uc],
154156
[self.subc(self.fx.ub, self.fx.uc)],
155-
[self.fx.uninhabited, self.fx.uninhabited],
157+
[self.fx.a_uninhabited, self.fx.a_uninhabited],
156158
[],
157159
allow_polymorphic=True,
158160
)

mypy/test/typefixture.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def make_type_var(
7878
self.anyt = AnyType(TypeOfAny.special_form)
7979
self.nonet = NoneType()
8080
self.uninhabited = UninhabitedType()
81+
self.a_uninhabited = UninhabitedType()
82+
self.a_uninhabited.ambiguous = True
8183

8284
# Abstract class TypeInfos
8385

mypy/types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,10 +1236,10 @@ def accept(self, visitor: TypeVisitor[T]) -> T:
12361236
return visitor.visit_uninhabited_type(self)
12371237

12381238
def __hash__(self) -> int:
1239-
return hash(UninhabitedType)
1239+
return hash((UninhabitedType, self.ambiguous))
12401240

12411241
def __eq__(self, other: object) -> bool:
1242-
return isinstance(other, UninhabitedType)
1242+
return isinstance(other, UninhabitedType) and other.ambiguous == self.ambiguous
12431243

12441244
def serialize(self) -> JsonDict:
12451245
return {".class": "UninhabitedType"}

test-data/unit/check-generic-subtyping.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,20 @@ s, s = Nums() # E: Incompatible types in assignment (expression has type "int",
753753
[builtins fixtures/for.pyi]
754754
[out]
755755

756+
[case testUninhabitedCacheChecksAmbiguous]
757+
# https://github.com/python/mypy/issues/19641
758+
from typing import Mapping, Never, TypeVar
759+
760+
M = TypeVar("M", bound=Mapping[str,object])
761+
762+
def get(arg: M, /) -> M:
763+
return arg
764+
765+
get({})
766+
767+
def upcast(d: dict[Never, Never]) -> Mapping[str, object]:
768+
return d # E: Incompatible return value type (got "dict[Never, Never]", expected "Mapping[str, object]")
769+
[builtins fixtures/dict.pyi]
756770

757771
-- Variance
758772
-- --------

test-data/unit/check-inference.test

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3781,10 +3781,18 @@ from typing import Any, Dict, NoReturn, NoReturn, Union
37813781

37823782
def foo() -> Union[Dict[str, Any], Dict[int, Any]]:
37833783
return {}
3784+
[builtins fixtures/dict.pyi]
3785+
3786+
[case testExistingEmptyCollectionDoesNotUpcast]
3787+
from typing import Any, Dict, NoReturn, NoReturn, Union
37843788

37853789
empty: Dict[NoReturn, NoReturn]
3790+
3791+
def foo() -> Dict[str, Any]:
3792+
return empty # E: Incompatible return value type (got "dict[Never, Never]", expected "dict[str, Any]")
3793+
37863794
def bar() -> Union[Dict[str, Any], Dict[int, Any]]:
3787-
return empty
3795+
return empty # E: Incompatible return value type (got "dict[Never, Never]", expected "Union[dict[str, Any], dict[int, Any]]")
37883796
[builtins fixtures/dict.pyi]
37893797

37903798
[case testUpperBoundInferenceFallbackNotOverused]

0 commit comments

Comments
 (0)