mypy icon indicating copy to clipboard operation
mypy copied to clipboard

false positive: "Overloaded function signatures ... overlap with incompatible return types"

Open finite-state-machine opened this issue 2 years ago • 5 comments

Bug Report

Code of a certain form causes a false positive error:

error: Overloaded function signatures 1 and 2 overlap with incompatible return types  [misc]

To Reproduce

Gist: mypy-play.net

from typing import (
        Literal,
        overload,
        Sequence,
        TypeVar,
        Union,
        )

T1 = TypeVar('T1')
T2 = TypeVar('T2')

@overload
def func(a1: Literal[True], a2: T1) -> Union[T1, Sequence[T2]]: ...
    # error: Overloaded function signatures 1 and 2 overlap with incompatible return types  [misc]

@overload
def func(a1: bool, a2: T1) -> Union[T1, Sequence[T2], T2]: ...

def func(  # type: ignore[empty-body]
        a1: bool, a2: T1) -> Union[T1, Sequence[T2], T2]:
    ...

Expected Behavior

There should be no errors.

  • the second signature's arguments are clearly broader than the first's (bool vs. Literal[True])
  • the second signature's return type is clearly broader than the first's (Union[...] vs. Union[..., T2])

Actual Behavior

An error is issued:

error: Overloaded function signatures 1 and 2 overlap with incompatible return types  [misc]

Additional notes

This example is fairly minimal:

  • removing the a2 argument from all three declarations causes the problem to disappear
  • removing the Sequence[T2] clause from the return Union of all three declarations causes the problem to disappear

Your Environment

  • Mypy version used: 0.910, 1.8.0
  • Mypy command-line flags: (none)
  • Mypy configuration options from mypy.ini (and other config files): (none)
  • Python version used: 3.8, 3.12

finite-state-machine avatar Sep 21 '21 21:09 finite-state-machine

The following example may be related. It is something we are trying to get working with respect to pandas stubs supplied by Microsoft with VS Code. Full discussion https://github.com/microsoft/python-type-stubs/issues/87

Goal is to have two parameters i1 that is bool and cs that is Optional[int] that may or may not be specified. Issue with mypy is that it is saying that None and int overlap. Goal of the code below is to have results corresponding to the table below:

i1 cs Return Type
Not specified Not specified int
False Not specified int
False None int
False An integer str
True Not specified str
True None str
True An integer str
Not specified None int
Not specified An integer str
@overload
def myfun(fake: str, i1: Literal[True], cs: Union[int, None] = ...) -> str:
    ...


@overload
def myfun(fake: str, i1: Literal[False], cs: int) -> str:
    ...


@overload
def myfun(fake: str, i1: Literal[False] = ..., cs: None = ...) -> int:
    ...


@overload
def myfun(fake: str, i1: bool = ..., cs: int = ...) -> str:
    ...


def myfun(fake: str, i1: bool = False, cs: Optional[int] = None) -> Union[str, int]:
    print(f"i1 is {i1} cs is {cs} result is ", end="")
    if i1:
        if cs is not None:
            return "TextFileReader"
        else:
            return "TextFileReader"
    else:
        if cs is not None:
            return "TextFileReader"
        else:
            return -1


res1: int = myfun("meh")
res2: int = myfun("meh", i1=False)
res3: int = myfun("meh", i1=False, cs=None)
res4: str = myfun("meh", i1=False, cs=23)
res5: str = myfun("meh", i1=True)
res6: str = myfun("meh", i1=True, cs=None)
res7: str = myfun("meh", i1=True, cs=23)
res8: int = myfun("meh", cs=None)
res9: str = myfun("meh", cs=23)

mypy version 0.910 complains as follows:

error: Overloaded function signatures 3 and 4 overlap with incompatible return types

The pyright type checker does not have this complaint.

Dr-Irv avatar Oct 12 '21 22:10 Dr-Irv

I think what's confusing mypy is the fact that cs has a default argument in overloads 3 and 4. We can eliminate that requirement if i1 and cs are keyword-only parameters by making the following change. This works fine in both mypy and pyright.

@overload
def myfun(fake: str, *, i1: Literal[True], cs: int | None = ...) -> str:
    ...


@overload
def myfun(fake: str, *, i1: Literal[False], cs: int) -> str:
    ...


@overload
def myfun(fake: str, *, i1: Literal[False] = ..., cs: None = ...) -> int:
    ...


@overload
def myfun(fake: str, *, i1: bool = ..., cs: int) -> str:
    ...


def myfun(fake: str, i1: bool = False, cs: Optional[int] = None) -> Union[str, int]:
    print(f"i1 is {i1} cs is {cs} result is ", end="")
    if i1:
        if cs is not None:
            return "TextFileReader"
        else:
            return "TextFileReader"
    else:
        if cs is not None:
            return "TextFileReader"
        else:
            return -1


res1: int = myfun("meh")
res2: int = myfun("meh", i1=False)
res3: int = myfun("meh", i1=False, cs=None)
res4: str = myfun("meh", i1=False, cs=23)
res5: str = myfun("meh", i1=True)
res6: str = myfun("meh", i1=True, cs=None)
res7: str = myfun("meh", i1=True, cs=23)
res8: int = myfun("meh", cs=None)
res9: str = myfun("meh", cs=23)

erictraut avatar Oct 13 '21 00:10 erictraut

  • verified to affect mypy 1.8.0 on Python 3.12
  • refreshed reproduction case to account for an unrelated issue (fixed), new warning (empty-body)
  • added mypy-play[.]net gist link to bug description

finite-state-machine avatar Feb 11 '24 14:02 finite-state-machine

Possibly related: https://github.com/python/mypy/issues/6580

jamesbraza avatar Mar 12 '24 21:03 jamesbraza

I think this is another example:

rpgoldman avatar May 02 '24 00:05 rpgoldman