Skip to content

Commit 3ad6157

Browse files
Fix match statement narrowing for self-matching class patterns with literal arguments (#21918)
Fixes #21780 This was likely because join_types was being used to combine two possible remaining types (e.g. tuple[int, int] and int), and it would collapse those into object instead of keeping both, which is why mypy couldn't narrow down to a bottom type (Never). This PR replaces join_types with make_simplified_union, and adds a regression test based on the repro in the issue. Could be a better fix than this, I would welcome it.
1 parent 4b6ec9d commit 3ad6157

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

mypy/checkpattern.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
605605
if not is_uninhabited(pattern_type.type):
606606
return PatternType(
607607
pattern_type.type,
608-
join_types(rest_type, pattern_type.rest_type),
608+
make_simplified_union([rest_type, pattern_type.rest_type]),
609609
pattern_type.captures,
610610
)
611611
captures = pattern_type.captures

test-data/unit/check-python310.test

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,32 @@ match m:
19051905
case b:
19061906
reveal_type(b) # N: Revealed type is "builtins.int"
19071907

1908+
[case testMatchClassPatternLiteralNegativeNarrowing]
1909+
# flags: --strict-equality --warn-unreachable
1910+
# See: https://github.com/python/mypy/issues/21780
1911+
1912+
from typing import reveal_type, NoReturn
1913+
1914+
def assert_never(x: NoReturn) -> None: ...
1915+
1916+
def f(x: int | tuple[int, int] | None) -> float | None:
1917+
match x:
1918+
case None:
1919+
reveal_type(x) # N: Revealed type is "None"
1920+
return None
1921+
case int(0):
1922+
reveal_type(x) # N: Revealed type is "Literal[0]"
1923+
return 0.0
1924+
case int(bits):
1925+
reveal_type(x) # N: Revealed type is "builtins.int"
1926+
return bits / 8.0
1927+
case int(bits), int(seconds):
1928+
reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]"
1929+
return bits / 8.0 / seconds
1930+
case _:
1931+
assert_never(x) # E: Statement is unreachable
1932+
[builtins fixtures/ops.pyi]
1933+
19081934
[case testMatchExhaustiveReturn]
19091935
# flags: --strict-equality --warn-unreachable
19101936
def foo(value) -> int:

0 commit comments

Comments
 (0)