Skip to content

Commit 611ceaf

Browse files
fix else branch in match case with literals
1 parent 76194eb commit 611ceaf

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

mypy/subtypes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,6 +2136,11 @@ def covers_at_runtime(item: Type, supertype: Type) -> bool:
21362136
item = get_proper_type(item)
21372137
supertype = get_proper_type(supertype)
21382138

2139+
# Use last known value for Instance types, if available.
2140+
# This ensures that e.g. Literal["max"]? is covered by Literal["max"].
2141+
if isinstance(item, Instance) and item.last_known_value is not None:
2142+
item = item.last_known_value
2143+
21392144
# Since runtime type checks will ignore type arguments, erase the types.
21402145
if not (isinstance(supertype, FunctionLike) and supertype.is_type_obj()):
21412146
supertype = erase_type(supertype)

mypy/test/testtypes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ def test_simplified_union_with_literals(self) -> None:
601601
[fx.lit1_inst, fx.lit3_inst], UnionType([fx.lit1_inst, fx.lit3_inst])
602602
)
603603
self.assert_simplified_union([fx.lit1_inst, fx.uninhabited], fx.lit1_inst)
604-
self.assert_simplified_union([fx.lit1, fx.lit1_inst], fx.lit1)
604+
self.assert_simplified_union([fx.lit1, fx.lit1_inst], fx.lit1_inst)
605605
self.assert_simplified_union([fx.lit1, fx.lit2_inst], UnionType([fx.lit1, fx.lit2_inst]))
606606
self.assert_simplified_union([fx.lit1, fx.lit3_inst], UnionType([fx.lit1, fx.lit3_inst]))
607607

@@ -651,7 +651,9 @@ def test_simplified_union_with_mixed_str_literals(self) -> None:
651651
[fx.lit_str1, fx.lit_str2, fx.lit_str3_inst],
652652
UnionType([fx.lit_str1, fx.lit_str2, fx.lit_str3_inst]),
653653
)
654-
self.assert_simplified_union([fx.lit_str1, fx.lit_str1, fx.lit_str1_inst], fx.lit_str1)
654+
self.assert_simplified_union(
655+
[fx.lit_str1, fx.lit_str1, fx.lit_str1_inst], fx.lit_str1_inst
656+
)
655657

656658
def assert_simplified_union(self, original: list[Type], union: Type) -> None:
657659
assert_equal(make_simplified_union(original), union)

mypy/typeops.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,8 @@ def make_simplified_union(
572572
* [int, Any] -> Union[int, Any] (Any types are not simplified away!)
573573
* [Any, Any] -> Any
574574
* [int, Union[bytes, str]] -> Union[int, bytes, str]
575+
* [Literal[1]?, Literal[1]] -> Literal[1]?
576+
* Literal["max"]?, Literal["max", "sum"] -> Literal["max"]? | Literal["sum"]
575577
576578
Note: This must NOT be used during semantic analysis, since TypeInfos may not
577579
be fully initialized.
@@ -600,13 +602,32 @@ def make_simplified_union(
600602
):
601603
simplified_set = try_contracting_literals_in_union(simplified_set)
602604

603-
result = get_proper_type(UnionType.make_union(simplified_set, line, column))
605+
# Step 5: Combine Literals and Instances with LKVs, e.g. Literal[1]?, Literal[1] -> Literal[1]?
606+
new_items = []
607+
for item in simplified_set:
608+
if isinstance(item, LiteralType):
609+
# scan if there is an Instance with a last_known_value that matches
610+
for other in simplified_set:
611+
if (
612+
isinstance(other, Instance)
613+
and other.last_known_value is not None
614+
and item == other.last_known_value
615+
):
616+
# do not include item
617+
break
618+
else:
619+
new_items.append(item)
620+
else:
621+
# If the item is not a LiteralType, we can use it directly.
622+
new_items.append(item)
623+
624+
result = get_proper_type(UnionType.make_union(new_items, line, column))
604625

605626
nitems = len(items)
606627
if nitems > 1 and (
607628
nitems > 2 or not (type(items[0]) is NoneType or type(items[1]) is NoneType)
608629
):
609-
# Step 5: At last, we erase any (inconsistent) extra attributes on instances.
630+
# Step 6: At last, we erase any (inconsistent) extra attributes on instances.
610631

611632
# Initialize with None instead of an empty set as a micro-optimization. The set
612633
# is needed very rarely, so we try to avoid constructing it.

0 commit comments

Comments
 (0)