Skip to content

[overload][intersection-types] undetected LSP violation in multiple inheritance #15790

@randolf-scholz

Description

@randolf-scholz

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 (python/typing#213).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions