-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Allow None vs TypeVar overlap for overloads #15846
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
Changes from 4 commits
11c082e
494016a
05eb6ff
f4e78ea
123fea4
76ec63f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2185,36 +2185,63 @@ def bar2(*x: int) -> int: ... | |
| [builtins fixtures/tuple.pyi] | ||
|
|
||
| [case testOverloadDetectsPossibleMatchesWithGenerics] | ||
| from typing import overload, TypeVar, Generic | ||
| # flags: --strict-optional | ||
| from typing import overload, TypeVar, Generic, Optional, List | ||
|
|
||
| T = TypeVar('T') | ||
| # The examples below are unsafe, but it is a quite common pattern | ||
| # so we ignore the possibility of type variables taking value `None` | ||
| # for the purpose of overload overlap checks. | ||
|
|
||
| @overload | ||
| def foo(x: None, y: None) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | ||
| def foo(x: None, y: None) -> str: ... | ||
| @overload | ||
| def foo(x: T, y: T) -> int: ... | ||
| def foo(x): ... | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test also calling this to make sure we infer the return type correctly?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh well, this discovered an obvious flaw in my solution, for this @overload
def foo(x: None) -> None: ...
@overload
def foo(x: T) -> list[T]: ...
x: int | None
foo(x)we will continue to infer
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I think I have come up with some heuristics to force union math for |
||
|
|
||
| oi: Optional[int] | ||
| reveal_type(foo(None, None)) # N: Revealed type is "builtins.str" | ||
| reveal_type(foo(None, 42)) # N: Revealed type is "builtins.int" | ||
| reveal_type(foo(42, 42)) # N: Revealed type is "builtins.int" | ||
| reveal_type(foo(oi, None)) # N: Revealed type is "Union[builtins.int, builtins.str]" | ||
| reveal_type(foo(oi, 42)) # N: Revealed type is "builtins.int" | ||
| reveal_type(foo(oi, oi)) # N: Revealed type is "Union[builtins.int, builtins.str]" | ||
|
|
||
| @overload | ||
| def foo_list(x: None) -> None: ... | ||
| @overload | ||
| def foo_list(x: T) -> List[T]: ... | ||
| def foo_list(x): ... | ||
|
|
||
| reveal_type(foo_list(oi)) # N: Revealed type is "Union[builtins.list[builtins.int], None]" | ||
|
|
||
| # What if 'T' is 'object'? | ||
| @overload | ||
| def bar(x: None, y: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | ||
| def bar(x: None, y: int) -> str: ... | ||
| @overload | ||
| def bar(x: T, y: T) -> int: ... | ||
| def bar(x, y): ... | ||
|
|
||
| class Wrapper(Generic[T]): | ||
| @overload | ||
| def foo(self, x: None, y: None) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | ||
| def foo(self, x: None, y: None) -> str: ... | ||
| @overload | ||
| def foo(self, x: T, y: None) -> int: ... | ||
| def foo(self, x): ... | ||
|
|
||
| @overload | ||
| def bar(self, x: None, y: int) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | ||
| def bar(self, x: None, y: int) -> str: ... | ||
| @overload | ||
| def bar(self, x: T, y: T) -> int: ... | ||
| def bar(self, x, y): ... | ||
|
|
||
| @overload | ||
| def baz(x: str, y: str) -> str: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | ||
| @overload | ||
| def baz(x: T, y: T) -> int: ... | ||
| def baz(x): ... | ||
| [builtins fixtures/tuple.pyi] | ||
|
|
||
| [case testOverloadFlagsPossibleMatches] | ||
| from wrapper import * | ||
| [file wrapper.pyi] | ||
|
|
@@ -3996,7 +4023,7 @@ T = TypeVar('T') | |
|
|
||
| class FakeAttribute(Generic[T]): | ||
| @overload | ||
| def dummy(self, instance: None, owner: Type[T]) -> 'FakeAttribute[T]': ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types | ||
| def dummy(self, instance: None, owner: Type[T]) -> 'FakeAttribute[T]': ... | ||
| @overload | ||
| def dummy(self, instance: T, owner: Type[T]) -> int: ... | ||
| def dummy(self, instance: Optional[T], owner: Type[T]) -> Union['FakeAttribute[T]', int]: ... | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.