From 5180c973ffb8e9912b1e66c30024700f3ccf7fe9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 11 Mar 2023 14:10:43 +0000 Subject: [PATCH 1/3] Fix crash on single item union of alias --- mypy/expandtype.py | 3 ++- test-data/unit/check-type-aliases.test | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 7933283b24d6..479a9959d19d 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -453,9 +453,10 @@ def visit_union_type(self, t: UnionType) -> Type: # After substituting for type variables in t.items, some resulting types # might be subtypes of others, however calling make_simplified_union() # can cause recursion, so we just remove strict duplicates. - return UnionType.make_union( + simplified = UnionType.make_union( remove_trivial(flatten_nested_unions(expanded)), t.line, t.column ) + return simplified def visit_partial_type(self, t: PartialType) -> Type: return t diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index d7cccd2d6ba6..f398bec081e1 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1028,3 +1028,18 @@ RHSAlias3: type = tuple[int, ...] WrongTypeElement = str | tuple[float, 1] # E: Invalid type: try using Literal[1] instead? WrongEllipsis = str | tuple[float, float, ...] # E: Unexpected "..." [builtins fixtures/tuple.pyi] + +[case testNoCompiledCrashOnSingleItemUnion] +# flags: --no-strict-optional +from typing import Callable, Union, Generic, TypeVar + +Alias = Callable[[], int] + +T = TypeVar("T") +class C(Generic[T]): + attr: Union[Alias, None] = None + + @classmethod + def test(cls) -> None: + cls.attr +[builtins fixtures/classmethod.pyi] From 32ae6d7f3daf175e4966af068f8c3f9fd39a9fb7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 11 Mar 2023 14:47:26 +0000 Subject: [PATCH 2/3] Apply the fix --- mypy/expandtype.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 479a9959d19d..ce7fc5804249 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -456,7 +456,12 @@ def visit_union_type(self, t: UnionType) -> Type: simplified = UnionType.make_union( remove_trivial(flatten_nested_unions(expanded)), t.line, t.column ) - return simplified + # This call to get_proper_type() is unfortunate but is required to preserve + # the invariant that ProperType will stay ProperType after applying expand_type(), + # otherwise a single item union of a type alias will break it. Note this should not + # cause infinite recursion since pathological aliases like A = Union[A, B] are + # banned at the semantic analysis level. + return get_proper_type(simplified) def visit_partial_type(self, t: PartialType) -> Type: return t From 9f0dc986369a2ded1c5481abc53461a4c91cb98a Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 12 Mar 2023 00:22:23 +0000 Subject: [PATCH 3/3] Fix test name --- test-data/unit/check-type-aliases.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index f398bec081e1..9dd56ad309f3 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1029,7 +1029,7 @@ WrongTypeElement = str | tuple[float, 1] # E: Invalid type: try using Literal[1 WrongEllipsis = str | tuple[float, float, ...] # E: Unexpected "..." [builtins fixtures/tuple.pyi] -[case testNoCompiledCrashOnSingleItemUnion] +[case testCompiledNoCrashOnSingleItemUnion] # flags: --no-strict-optional from typing import Callable, Union, Generic, TypeVar