Skip to content

Commit c3ed5e0

Browse files
authored
Flatten union before contracting literals when checking subtyping (#18898)
Fixes #18896
1 parent c7ea011 commit c3ed5e0

File tree

3 files changed

+28
-1
lines changed

3 files changed

+28
-1
lines changed

mypy/subtypes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
UnionType,
6868
UnpackType,
6969
find_unpack_in_list,
70+
flatten_nested_unions,
7071
get_proper_type,
7172
is_named_instance,
7273
split_with_prefix_and_suffix,
@@ -327,7 +328,9 @@ def _is_subtype(
327328
and isinstance(left, Instance)
328329
and (left.type.is_enum or left.type.fullname == "builtins.bool")
329330
):
330-
right = UnionType(mypy.typeops.try_contracting_literals_in_union(right.items))
331+
right = UnionType(
332+
mypy.typeops.try_contracting_literals_in_union(flatten_nested_unions(right.items))
333+
)
331334
if proper_subtype:
332335
is_subtype_of_item = any(
333336
is_proper_subtype(orig_left, item, subtype_context=subtype_context)

mypy/typeops.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,8 @@ class Status(Enum):
10691069
def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType]:
10701070
"""Contracts any literal types back into a sum type if possible.
10711071
1072+
Requires a flattened union and does not descend into children.
1073+
10721074
Will replace the first instance of the literal with the sum type and
10731075
remove all others.
10741076

test-data/unit/check-literal.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,6 +2765,28 @@ reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.A]"
27652765
reveal_type(y) # N: Revealed type is "Literal[__main__.Foo.A]"
27662766
[builtins fixtures/tuple.pyi]
27672767

2768+
[case testLiteralUnionEnumAliasAssignable]
2769+
from enum import Enum
2770+
from typing import Literal, Union
2771+
2772+
class E(Enum):
2773+
A = 'a'
2774+
B = 'b'
2775+
C = 'c'
2776+
2777+
A = Literal[E.A]
2778+
B = Literal[E.B, E.C]
2779+
2780+
def f(x: Union[A, B]) -> None: ...
2781+
def f2(x: Union[A, Literal[E.B, E.C]]) -> None: ...
2782+
def f3(x: Union[Literal[E.A], B]) -> None: ...
2783+
2784+
def main(x: E) -> None:
2785+
f(x)
2786+
f2(x)
2787+
f3(x)
2788+
[builtins fixtures/tuple.pyi]
2789+
27682790
[case testStrictEqualityLiteralTrueVsFalse]
27692791
# mypy: strict-equality
27702792

0 commit comments

Comments
 (0)