From 87e8de68453b64be000062e159be0bbbe521c597 Mon Sep 17 00:00:00 2001 From: STerliakov Date: Mon, 7 Apr 2025 21:25:30 +0200 Subject: [PATCH] Flatten union before contracting literals when checking subtyping --- mypy/subtypes.py | 5 ++++- mypy/typeops.py | 2 ++ test-data/unit/check-literal.test | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 41bb4601e23f..71b8b0ba59f5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -67,6 +67,7 @@ UnionType, UnpackType, find_unpack_in_list, + flatten_nested_unions, get_proper_type, is_named_instance, split_with_prefix_and_suffix, @@ -327,7 +328,9 @@ def _is_subtype( and isinstance(left, Instance) and (left.type.is_enum or left.type.fullname == "builtins.bool") ): - right = UnionType(mypy.typeops.try_contracting_literals_in_union(right.items)) + right = UnionType( + mypy.typeops.try_contracting_literals_in_union(flatten_nested_unions(right.items)) + ) if proper_subtype: is_subtype_of_item = any( is_proper_subtype(orig_left, item, subtype_context=subtype_context) diff --git a/mypy/typeops.py b/mypy/typeops.py index 06ecc0fb3fda..bcf946900563 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -1069,6 +1069,8 @@ class Status(Enum): def try_contracting_literals_in_union(types: Sequence[Type]) -> list[ProperType]: """Contracts any literal types back into a sum type if possible. + Requires a flattened union and does not descend into children. + Will replace the first instance of the literal with the sum type and remove all others. diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 88c02f70488c..f36eff28f33f 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -2765,6 +2765,28 @@ reveal_type(x) # N: Revealed type is "Literal[__main__.Foo.A]" reveal_type(y) # N: Revealed type is "Literal[__main__.Foo.A]" [builtins fixtures/tuple.pyi] +[case testLiteralUnionEnumAliasAssignable] +from enum import Enum +from typing import Literal, Union + +class E(Enum): + A = 'a' + B = 'b' + C = 'c' + +A = Literal[E.A] +B = Literal[E.B, E.C] + +def f(x: Union[A, B]) -> None: ... +def f2(x: Union[A, Literal[E.B, E.C]]) -> None: ... +def f3(x: Union[Literal[E.A], B]) -> None: ... + +def main(x: E) -> None: + f(x) + f2(x) + f3(x) +[builtins fixtures/tuple.pyi] + [case testStrictEqualityLiteralTrueVsFalse] # mypy: strict-equality