From f43fd8fecf9f4dc58614297060983937483f2172 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 19 Jan 2017 17:38:34 +0000 Subject: [PATCH] Fix simplification of some unions In particular, this helps with classes that have Any base classes. This is enough to fix #2712. This is almost a subset of #2714 and should land before that, as this should cause less disruption. --- mypy/types.py | 7 ++++- test-data/unit/check-optional.test | 19 ++++++++++++ test-data/unit/check-unions.test | 49 ++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/mypy/types.py b/mypy/types.py index 32d7c8340a6c..3d27d52d8a87 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1034,13 +1034,18 @@ def make_simplified_union(items: List[Type], line: int = -1, column: int = -1) - return AnyType() from mypy.subtypes import is_subtype + from mypy.sametypes import is_same_type + removed = set() # type: Set[int] for i, ti in enumerate(items): if i in removed: continue # Keep track of the truishness info for deleted subtypes which can be relevant cbt = cbf = False for j, tj in enumerate(items): - if i != j and is_subtype(tj, ti): + if (i != j + and is_subtype(tj, ti) + and (not (isinstance(tj, Instance) and tj.type.fallback_to_any) + or is_same_type(ti, tj))): removed.add(j) cbt = cbt or tj.can_be_true cbf = cbf or tj.can_be_false diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index eca81bf8d37f..09fb4b5fdc91 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -525,3 +525,22 @@ f = None # type: Optional[Callable[[int], None]] f = lambda x: None f(0) [builtins fixtures/function.pyi] + +[case testUnionSimplificationWithStrictOptional] +from typing import Any, TypeVar, Union +class C(Any): pass +T = TypeVar('T') +S = TypeVar('S') +def u(x: T, y: S) -> Union[S, T]: pass +a = None # type: Any + +# Test both orders +reveal_type(u(C(), None)) # E: Revealed type is 'Union[builtins.None, __main__.C*]' +reveal_type(u(None, C())) # E: Revealed type is 'Union[__main__.C*, builtins.None]' + +# This will be fixed later +reveal_type(u(a, None)) # E: Revealed type is 'Any' +reveal_type(u(None, a)) # E: Revealed type is 'Any' + +reveal_type(u(1, None)) # E: Revealed type is 'Union[builtins.None, builtins.int*]' +reveal_type(u(None, 1)) # E: Revealed type is 'Union[builtins.int*, builtins.None]' diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index e1a30434bffe..c51cb06a0002 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -168,3 +168,52 @@ if foo(): def g(x: Union[int, str, bytes]) -> None: pass else: def g(x: Union[int, str]) -> None: pass # E: All conditional function variants must have identical signatures + +[case testUnionSimplificationSpecialCases] +from typing import Any, TypeVar, Union + +class C(Any): pass + +T = TypeVar('T') +S = TypeVar('S') +def u(x: T, y: S) -> Union[S, T]: pass + +a = None # type: Any + +reveal_type(u(C(), None)) # E: Revealed type is '__main__.C*' +reveal_type(u(None, C())) # E: Revealed type is '__main__.C*' + +# This will be fixed later +reveal_type(u(C(), a)) # E: Revealed type is 'Any' +reveal_type(u(a, C())) # E: Revealed type is 'Any' + +reveal_type(u(C(), C())) # E: Revealed type is '__main__.C*' +reveal_type(u(a, a)) # E: Revealed type is 'Any' + +[case testUnionSimplificationSpecialCase2] +from typing import Any, TypeVar, Union + +class C(Any): pass + +T = TypeVar('T') +S = TypeVar('S') +def u(x: T, y: S) -> Union[S, T]: pass + +def f(x: T) -> None: + reveal_type(u(C(), x)) # E: Revealed type is 'Union[T`-1, __main__.C*]' + reveal_type(u(x, C())) # E: Revealed type is 'Union[__main__.C*, T`-1]' + +[case testUnionSimplificationSpecialCase3] +from typing import Any, TypeVar, Generic, Union + +class C(Any): pass + +V = TypeVar('V') +T = TypeVar('T') + +class M(Generic[V]): + def get(self, default: T) -> Union[V, T]: ... + +def f(x: M[C]) -> None: + y = x.get(None) + reveal_type(y) # E: Revealed type is '__main__.C'