Skip to content

Commit 2fbfb60

Browse files
authored
Fix inference with UninhabitedType (#16994)
At the moment, inference fails if an empty dict is used (without annotation) as one of the types. It's because the constraint solver can't resolve `dict[str, int]` and `dict[Never, Never]`. However in this case it's more reasonable to interpret the empty dict as `dict[Any, Any]` and just using the first type instead. That matches the behavior of pyright. ```py T = TypeVar("T") class A(Generic[T]): ... def func1(a: A[T], b: T) -> T: ... def a1(a: A[Dict[str, int]]) -> None: reveal_type(func1(a, {})) ``` ``` # before main: error: Cannot infer type argument 1 of "func1" (diff) main: note: Revealed type is "Any" (diff) # after main: note: Revealed type is "builtins.dict[builtins.str, builtins.int]" ```
1 parent 2f0f8f2 commit 2fbfb60

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

mypy/join.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,17 @@ def join_instances(self, t: Instance, s: Instance) -> ProperType:
108108
# TODO: contravariant case should use meet but pass seen instances as
109109
# an argument to keep track of recursive checks.
110110
elif type_var.variance in (INVARIANT, CONTRAVARIANT):
111-
if not is_equivalent(ta, sa):
111+
if isinstance(ta_proper, UninhabitedType) and not ta_proper.is_noreturn:
112+
new_type = sa
113+
elif isinstance(sa_proper, UninhabitedType) and not sa_proper.is_noreturn:
114+
new_type = ta
115+
elif not is_equivalent(ta, sa):
112116
self.seen_instances.pop()
113117
return object_from_instance(t)
114-
# If the types are different but equivalent, then an Any is involved
115-
# so using a join in the contravariant case is also OK.
116-
new_type = join_types(ta, sa, self)
118+
else:
119+
# If the types are different but equivalent, then an Any is involved
120+
# so using a join in the contravariant case is also OK.
121+
new_type = join_types(ta, sa, self)
117122
elif isinstance(type_var, TypeVarTupleType):
118123
new_type = get_proper_type(join_types(ta, sa, self))
119124
# Put the joined arguments back into instance in the normal form:

test-data/unit/check-inference.test

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3813,3 +3813,37 @@ def m1() -> float: ...
38133813
def m2() -> float: ...
38143814
reveal_type(Combine(m1, m2)) # N: Revealed type is "builtins.float"
38153815
[builtins fixtures/list.pyi]
3816+
3817+
[case testInferenceWithUninhabitedType]
3818+
from typing import Dict, Generic, List, Never, TypeVar
3819+
3820+
T = TypeVar("T")
3821+
3822+
class A(Generic[T]): ...
3823+
class B(Dict[T, T]): ...
3824+
3825+
def func1(a: A[T], b: T) -> T: ...
3826+
def func2(a: T, b: A[T]) -> T: ...
3827+
3828+
def a1(a: A[Dict[str, int]]) -> None:
3829+
reveal_type(func1(a, {})) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
3830+
reveal_type(func2({}, a)) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
3831+
3832+
def a2(check: bool, a: B[str]) -> None:
3833+
reveal_type(a if check else {}) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]"
3834+
3835+
def a3() -> None:
3836+
a = {} # E: Need type annotation for "a" (hint: "a: Dict[<type>, <type>] = ...")
3837+
b = {1: {}} # E: Need type annotation for "b"
3838+
c = {1: {}, 2: {"key": {}}} # E: Need type annotation for "c"
3839+
reveal_type(a) # N: Revealed type is "builtins.dict[Any, Any]"
3840+
reveal_type(b) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[Any, Any]]"
3841+
reveal_type(c) # N: Revealed type is "builtins.dict[builtins.int, builtins.dict[builtins.str, builtins.dict[Any, Any]]]"
3842+
3843+
def a4(x: List[str], y: List[Never]) -> None:
3844+
z1 = [x, y]
3845+
z2 = [y, x]
3846+
reveal_type(z1) # N: Revealed type is "builtins.list[builtins.object]"
3847+
reveal_type(z2) # N: Revealed type is "builtins.list[builtins.object]"
3848+
z1[1].append("asdf") # E: "object" has no attribute "append"
3849+
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)