Repository: python/mypy

Issue:
Mypy thinks `dict[Never, Never]` is a subtype of `Mapping[str, object]`
This behaves as expected: https://mypy-play.net/?mypy=latest&python=3.12&gist=8718e96550599dc3ae48a13879005762

```python
from typing import Mapping, Never

def upcast(d: dict[Never, Never]) -> Mapping[str, object]:
    return d  # E: [return-value] (✅ expected)
```

But it breaks here (due to caching?) https://mypy-play.net/?mypy=latest&python=3.12&gist=6199ba50b6b02e2350e70cb3a8ff4bba

```python
from typing import Mapping, Never

def get[M: Mapping[str, object]](arg: M, /) -> M:
    return arg

reveal_type(get({}))  # dict[Never, Never] (❌ incorrect)

def upcast(d: dict[Never, Never]) -> Mapping[str, object]:
    return d  # <-- No error anymore 😱
```


Discussion:

Patch:
diff --git a/mypy/types.py b/mypy/types.py
index d7dd3e1f2dce..26c5b474ba6c 100644
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -1236,10 +1236,10 @@ def accept(self, visitor: TypeVisitor[T]) -> T:
         return visitor.visit_uninhabited_type(self)
 
     def __hash__(self) -> int:
-        return hash(UninhabitedType)
+        return hash((UninhabitedType, self.ambiguous))
 
     def __eq__(self, other: object) -> bool:
-        return isinstance(other, UninhabitedType)
+        return isinstance(other, UninhabitedType) and other.ambiguous == self.ambiguous
 
     def serialize(self) -> JsonDict:
         return {".class": "UninhabitedType"}


Test patch:
diff --git a/mypy/test/testsolve.py b/mypy/test/testsolve.py
index 6566b03ef5e9..d60b2cb3fcc5 100644
--- a/mypy/test/testsolve.py
+++ b/mypy/test/testsolve.py
@@ -64,12 +64,14 @@ def test_multiple_variables(self) -> None:
         )
 
     def test_no_constraints_for_var(self) -> None:
-        self.assert_solve([self.fx.t], [], [self.fx.uninhabited])
-        self.assert_solve([self.fx.t, self.fx.s], [], [self.fx.uninhabited, self.fx.uninhabited])
+        self.assert_solve([self.fx.t], [], [self.fx.a_uninhabited])
+        self.assert_solve(
+            [self.fx.t, self.fx.s], [], [self.fx.a_uninhabited, self.fx.a_uninhabited]
+        )
         self.assert_solve(
             [self.fx.t, self.fx.s],
             [self.supc(self.fx.s, self.fx.a)],
-            [self.fx.uninhabited, self.fx.a],
+            [self.fx.a_uninhabited, self.fx.a],
         )
 
     def test_simple_constraints_with_dynamic_type(self) -> None:
@@ -116,7 +118,7 @@ def test_poly_no_constraints(self) -> None:
         self.assert_solve(
             [self.fx.t, self.fx.u],
             [],
-            [self.fx.uninhabited, self.fx.uninhabited],
+            [self.fx.a_uninhabited, self.fx.a_uninhabited],
             allow_polymorphic=True,
         )
 
@@ -152,7 +154,7 @@ def test_poly_free_pair_with_bounds_uninhabited(self) -> None:
         self.assert_solve(
             [self.fx.ub, self.fx.uc],
             [self.subc(self.fx.ub, self.fx.uc)],
-            [self.fx.uninhabited, self.fx.uninhabited],
+            [self.fx.a_uninhabited, self.fx.a_uninhabited],
             [],
             allow_polymorphic=True,
         )
diff --git a/mypy/test/typefixture.py b/mypy/test/typefixture.py
index d6c904732b17..0defcdaebc99 100644
--- a/mypy/test/typefixture.py
+++ b/mypy/test/typefixture.py
@@ -78,6 +78,8 @@ def make_type_var(
         self.anyt = AnyType(TypeOfAny.special_form)
         self.nonet = NoneType()
         self.uninhabited = UninhabitedType()
+        self.a_uninhabited = UninhabitedType()
+        self.a_uninhabited.ambiguous = True
 
         # Abstract class TypeInfos
 
diff --git a/test-data/unit/check-generic-subtyping.test b/test-data/unit/check-generic-subtyping.test
index f65ef3975852..ee5bcccf8ace 100644
--- a/test-data/unit/check-generic-subtyping.test
+++ b/test-data/unit/check-generic-subtyping.test
@@ -753,6 +753,20 @@ s, s = Nums() # E: Incompatible types in assignment (expression has type "int",
 [builtins fixtures/for.pyi]
 [out]
 
+[case testUninhabitedCacheChecksAmbiguous]
+# https://github.com/python/mypy/issues/19641
+from typing import Mapping, Never, TypeVar
+
+M = TypeVar("M", bound=Mapping[str,object])
+
+def get(arg: M, /) -> M:
+    return arg
+
+get({})
+
+def upcast(d: dict[Never, Never]) -> Mapping[str, object]:
+    return d  # E: Incompatible return value type (got "dict[Never, Never]", expected "Mapping[str, object]")
+[builtins fixtures/dict.pyi]
 
 -- Variance
 -- --------
diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test
index 53efcc0d22e3..63278d6c4547 100644
--- a/test-data/unit/check-inference.test
+++ b/test-data/unit/check-inference.test
@@ -3781,10 +3781,18 @@ from typing import Any, Dict, NoReturn, NoReturn, Union
 
 def foo() -> Union[Dict[str, Any], Dict[int, Any]]:
     return {}
+[builtins fixtures/dict.pyi]
+
+[case testExistingEmptyCollectionDoesNotUpcast]
+from typing import Any, Dict, NoReturn, NoReturn, Union
 
 empty: Dict[NoReturn, NoReturn]
+
+def foo() -> Dict[str, Any]:
+    return empty  # E: Incompatible return value type (got "dict[Never, Never]", expected "dict[str, Any]")
+
 def bar() -> Union[Dict[str, Any], Dict[int, Any]]:
-    return empty
+    return empty  # E: Incompatible return value type (got "dict[Never, Never]", expected "Union[dict[str, Any], dict[int, Any]]")
 [builtins fixtures/dict.pyi]
 
 [case testUpperBoundInferenceFallbackNotOverused]