Skip to content

Commit 967f590

Browse files
authored
Checks Instance and Literal subtypes correctly, refs #11232 (#11236)
Closes #11232 Closes #7399
1 parent ed0cc78 commit 967f590

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

mypy/subtypes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ def visit_instance(self, left: Instance) -> bool:
287287
return True
288288
if isinstance(item, Instance):
289289
return is_named_instance(item, 'builtins.object')
290+
if isinstance(right, LiteralType) and left.last_known_value is not None:
291+
return self._is_subtype(left.last_known_value, right)
290292
if isinstance(right, CallableType):
291293
# Special case: Instance can be a subtype of Callable.
292294
call = find_member('__call__', left, left, is_operator=True)

test-data/unit/check-literal.test

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2687,11 +2687,8 @@ def force2(x: Tuple[Literal[1], Literal[2]]) -> None: pass
26872687
reveal_type(a) # N: Revealed type is "Literal[1]?"
26882688
reveal_type(b) # N: Revealed type is "Tuple[Literal[1]?, Literal[2]?]"
26892689

2690-
# TODO: This test seems somewhat broken and might need a rewrite (and a fix somewhere in mypy).
2691-
# See https://github.com/python/mypy/issues/7399#issuecomment-554188073 for more context.
2692-
force1(reveal_type(a)) # N: Revealed type is "Literal[1]"
2693-
force2(reveal_type(b)) # E: Argument 1 to "force2" has incompatible type "Tuple[int, int]"; expected "Tuple[Literal[1], Literal[2]]" \
2694-
# N: Revealed type is "Tuple[Literal[1]?, Literal[2]?]"
2690+
force1(a) # ok
2691+
force2(b) # ok
26952692
[builtins fixtures/tuple.pyi]
26962693
[out]
26972694

@@ -3304,3 +3301,48 @@ else:
33043301
reveal_type(w) # E: Statement is unreachable
33053302

33063303
[builtins fixtures/bool.pyi]
3304+
3305+
[case testLiteralAndInstanceSubtyping]
3306+
# https://github.com/python/mypy/issues/7399
3307+
# https://github.com/python/mypy/issues/11232
3308+
from typing import Tuple, Union
3309+
from typing_extensions import Literal, Final
3310+
3311+
x: bool
3312+
3313+
def f() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]:
3314+
if x:
3315+
return (True, 5)
3316+
else:
3317+
return (False, 'oops')
3318+
3319+
reveal_type(f()) # N: Revealed type is "Union[Tuple[Literal[True], builtins.int], Tuple[Literal[False], builtins.str]]"
3320+
3321+
def does_work() -> Tuple[Literal[1]]:
3322+
x: Final = (1,)
3323+
return x
3324+
3325+
def also_works() -> Tuple[Literal[1]]:
3326+
x: Tuple[Literal[1]] = (1,)
3327+
return x
3328+
3329+
def invalid_literal_value() -> Tuple[Literal[1]]:
3330+
x: Final = (2,)
3331+
return x # E: Incompatible return value type (got "Tuple[int]", expected "Tuple[Literal[1]]")
3332+
3333+
def invalid_literal_type() -> Tuple[Literal[1]]:
3334+
x: Final = (True,)
3335+
return x # E: Incompatible return value type (got "Tuple[bool]", expected "Tuple[Literal[1]]")
3336+
3337+
def incorrect_return1() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]:
3338+
if x:
3339+
return (False, 5) # E: Incompatible return value type (got "Tuple[bool, int]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
3340+
else:
3341+
return (True, 'oops') # E: Incompatible return value type (got "Tuple[bool, str]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
3342+
3343+
def incorrect_return2() -> Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]:
3344+
if x:
3345+
return (bool(), 5) # E: Incompatible return value type (got "Tuple[bool, int]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
3346+
else:
3347+
return (bool(), 'oops') # E: Incompatible return value type (got "Tuple[bool, str]", expected "Union[Tuple[Literal[True], int], Tuple[Literal[False], str]]")
3348+
[builtins fixtures/bool.pyi]

0 commit comments

Comments
 (0)