diff --git a/mypy/typeops.py b/mypy/typeops.py index eb9d0d1474b8b..4a694b0a88a67 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -1153,6 +1153,12 @@ class Status(Enum): if isinstance(typ, Instance) and ( target_fullname is None or typ.type.fullname == target_fullname ): + if typ.last_known_value is not None: + # The expression is already known to hold this specific value, so keep it: expanding + # to all members of the sum type would forget it and break narrowing, e.g. matching a + # ``False`` literal subject against ``case False`` would not be exhaustive. + return typ.last_known_value + if typ.type.fullname == "builtins.bool": return UnionType([LiteralType(True, typ), LiteralType(False, typ)]) diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 33b952f48f49d..a75c4fb9efced 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -50,6 +50,34 @@ def foo(m: bool, x: int): reveal_type(m) # N: Revealed type is "builtins.bool" [builtins fixtures/primitives.pyi] +[case testMatchBoolLiteralSubjectIsExhaustive] +# flags: --strict-equality --warn-unreachable + +def f() -> int: + match False: + case True: + return 1 # E: Statement is unreachable + case False: + return 0 + +def g(b: bool) -> int: + match b: + case True: + return 1 + case False: + return 0 + +def h() -> int: + match False: + case True: + return 1 # E: Statement is unreachable + case False: + return 0 + case other: + reveal_type(other) # E: Statement is unreachable + return 2 +[builtins fixtures/primitives.pyi] + [case testMatchLiteralPatternUnreachable] # flags: --strict-equality --warn-unreachable m: int