Skip to content

Consider fallback instance promotions in is_overlapping_types() #7358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions mypy/meet.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
UninhabitedType, TypeType, TypeOfAny, Overloaded, FunctionLike, LiteralType,
ProperType, get_proper_type, get_proper_types
)
from mypy.subtypes import (
is_equivalent, is_subtype, is_protocol_implementation, is_callable_compatible,
is_proper_subtype,
)
from mypy.subtypes import is_equivalent, is_subtype, is_callable_compatible, is_proper_subtype
from mypy.erasetype import erase_type
from mypy.maptype import map_instance_to_supertype
from mypy.typeops import tuple_fallback
Expand Down Expand Up @@ -276,7 +273,13 @@ def _type_object_overlap(left: ProperType, right: ProperType) -> bool:
right = right.fallback

if isinstance(left, LiteralType) and isinstance(right, LiteralType):
return left == right
if left.value == right.value:
# If values are the same, we still need to check if fallbacks are overlapping,
# this is done below.
left = left.fallback
right = right.fallback
else:
return False
elif isinstance(left, LiteralType):
left = left.fallback
elif isinstance(right, LiteralType):
Expand All @@ -285,15 +288,13 @@ def _type_object_overlap(left: ProperType, right: ProperType) -> bool:
# Finally, we handle the case where left and right are instances.

if isinstance(left, Instance) and isinstance(right, Instance):
if left.type.is_protocol and is_protocol_implementation(right, left):
return True
if right.type.is_protocol and is_protocol_implementation(left, right):
# First we need to handle promotions and structural compatibility for instances
# that came as fallbacks, so simply call is_subtype() to avoid code duplication.
if (is_subtype(left, right, ignore_promotions=ignore_promotions)
or is_subtype(right, left, ignore_promotions=ignore_promotions)):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's somewhat unfortunate we end up needing to perform a subtype check again here (we already call is_proper_subtype earlier in the method).

But as you said, it doesn't seem like there's a clean way of working around this without repeating some of the promotion logic anyways...

return True

# Two unrelated types cannot be partially overlapping: they're disjoint.
# We don't need to handle promotions because they've already been handled
# by the calls to `is_subtype(...)` up above (and promotable types never
# have any generic arguments we need to recurse on).
if left.type.has_base(right.type.fullname()):
left = map_instance_to_supertype(left, right.type)
elif right.type.has_base(left.type.fullname()):
Expand All @@ -302,9 +303,6 @@ def _type_object_overlap(left: ProperType, right: ProperType) -> bool:
return False

if len(left.args) == len(right.args):
if not left.args:
# We can get here if the instance is in fact a fallback from another type.
return True
# Note: we don't really care about variance here, since the overlapping check
# is symmetric and since we want to return 'True' even for partial overlaps.
#
Expand Down
12 changes: 12 additions & 0 deletions test-data/unit/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -2303,6 +2303,18 @@ if f == 0: # E: Non-overlapping equality check (left operand type: "FileId", ri
...
[builtins fixtures/bool.pyi]

[case testStrictEqualityPromotionsLiterals]
# flags: --strict-equality --py2
from typing import Final

U_FOO = u'foo' # type: Final

if str() == U_FOO:
pass
assert u'foo' == 'foo'
assert u'foo' == u'bar' # E: Non-overlapping equality check (left operand type: "Literal[u'foo']", right operand type: "Literal[u'bar']")
[builtins_py2 fixtures/python2.pyi]

[case testUnimportedHintAny]
def f(x: Any) -> None: # E: Name 'Any' is not defined \
# N: Did you forget to import it from "typing"? (Suggestion: "from typing import Any")
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/fixtures/python2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ from typing import Generic, Iterable, TypeVar

class object:
def __init__(self) -> None: pass
def __eq__(self, other: object) -> bool: pass
def __ne__(self, other: object) -> bool: pass

class type:
def __init__(self, x) -> None: pass
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy