Skip to content

Commit

Permalink
Fix simplification of some unions (#2718)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
JukkaL authored and gvanrossum committed Jan 20, 2017
1 parent 5d17006 commit 9496d6f
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
7 changes: 6 additions & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions test-data/unit/check-optional.test
Original file line number Diff line number Diff line change
Expand Up @@ -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]'
49 changes: 49 additions & 0 deletions test-data/unit/check-unions.test
Original file line number Diff line number Diff line change
Expand Up @@ -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'

0 comments on commit 9496d6f

Please sign in to comment.