[overload][intersection-types] undetected LSP violation in multiple inheritance
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 20.6k
- Forks
- 3.3k
- PR merge metrics
- PR metrics pending
Description
Bug Report
Essentially, the bug occurs when trying to define a subclass of C≤A&B, and implementing a shared method via @overload:
class A:
def foo(self, x: Hashable) -> Hashable: ...
class B:
def foo(self, x: Sized) -> Sized: ...
class C(A, B):
@overload
def foo(self, x: Hashable) -> Hashable: ...
@overload
def foo(self, x: Sized) -> Sized: ...
C is not a proper subclass of A & B since C.foo(Hashable & Sized) yields Hashable, violating LSP for B. mypy does not detect this issue. To resolve this, an extra overload of the form def foo(x: Hashable & Sized) -> ... is needed.
To Reproduce
A concrete demo with non-trivial types.
from typing import overload
from collections.abc import Hashable, Sized
class A:
def foo(self, x: Hashable) -> int:
return hash(x)
class B:
def foo(self, x: Sized) -> str:
return f"x has {len(x)} elements"
class C(A, B):
@overload
def foo(self, x: Hashable) -> int: ...
@overload
def foo(self, x: Sized) -> str: ...
def foo(self, x): # type: ignore[no-untyped-def]
if isinstance(x, Hashable):
return A.foo(self, x)
if isinstance(x, Sized):
return B.foo(self, x)
# mypy does not complain about LSP
a_lsp: A = C()
b_lsp: B = C()
c: C = C()
# tuples are hashable and sized.
x: tuple[int, int, int] = (1,2,3)
# but the return types are incompatible!
reveal_type(a_lsp.foo(x)) # int
reveal_type(b_lsp.foo(x)) # str
reveal_type(c.foo(x)) # int, is not a subtype of str
This file parses even using --strict without raising any warnings/errors. (mypy-playground)
However, the inconsistency becomes obvious as c.foo(<tuple>) reveals as int, which is incompatible with b.foo(<tuple>).
Expected Behavior
@overload
def foo(self, x: Hashable) -> int: ...
@overload
def foo(self, x: Sized) -> str: ...
Are not type-safe overloads to satisfy LSP for both superclasses. Mypy should notice this and issue a warning. Likely, to solve this issue in a satisfactory manner, intersection-types are needed (https://github.com/python/typing/issues/213).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the supplied multiple-inheritance example with mypy under --strict, using the linked mypy-playground case and reveal_type results. Trace overload resolution and inherited-method compatibility to determine where the LSP violation is missed; done means the behavior is addressed with a clear diagnostic or an explicitly documented limitation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers, devtools
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100