microsoft / microsoft/pyright

False positive `InconsistentOverload` with generic class of tuple argument

Open
#11,145 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
15.6k
Forks
1.8k
Avg merge
12h 13m
Merged PRs (30d)
52

Description

The example below type checks both with `mypy` and `pyrefly`, but for `pyright` we get the following error message on the last `def fork[X, Y]`:

```
error: Overloaded implementation is not consistent with signature of overload 3
  Function return type "Fork[X@fork, tuple[Y1@fork, Y2@fork]]" is incompatible with type "Fork[X@fork, tuple[Y1@fork, ...]]"
    "Fork[X@fork, tuple[Y1@fork, Y2@fork]]" is not assignable to "Fork[X@fork, tuple[Y1@fork, ...]]"
      Type parameter "TupleOut@Fork" is covariant, but "tuple[Y1@fork, Y2@fork]" is not a subtype of "tuple[Y1@fork, ...]"
        "tuple[Y1@fork, Y2@fork]" is not assignable to "tuple[Y1@fork, ...]"
          Tuple entry 1 is incorrect type (reportInconsistentOverload)
```

I believe this is a bug, because:

1. The type ``Fork[X@fork, tuple[Y1@fork, ...]]`` does not make sense. Why does only `Y1` appear but not `Y2`?
2. If this were incorrect, why does it not complain already for the 4th overload, which has identical signature to the implementation?
3. What we should have is that the solver considers `tuple[Y1, Y2]` as a subtype of `tuple[Y, ...]`, where `Y1` and `Y2` come from the 3rd overload and `Y` is the type var bound to the implementation. But that just gives the constraint `Y1 <: Y` and `Y2 <: Y`, so `Y = Y1 | Y2` is a solution.

Code sample in [pyright playground](https://pyright-play.net/?pyrightVersion=1.1.405&pythonVersion=3.13&code=GYJw9gtgBALgngBwJYDsDmUkQWEMoCCKcANFAJIwCmIAhgEYA2VZAxrQM4xlgBuNjMLQAmAKFGtGnDlAAqdFB2C4IAbQAaZAJoBdAFyioRqMKrBYCpSoAUHKo2BkAHnqiaoAegCUUALQA%2BKC1XADow8UlpKAAxXABrDTJZAFcEZgB5ZJhXGFTmHWt5WkVlEDV3FLSqTJgdLwNjKBAAIlaCBDS4KAhkxhgkKotiqzKZGDBYAAsqKA5aCBnUBCyoYuEmqlyQFFXYPJmwczAs5ZgOENbm8UbTcwB9O9QkGAfVAFUyADUCw0bjOwcrmasRACQ%2Beyqqk%2BZDCIR0OmaZBgllKEA4rkoNAYzFURRKKneX3hZA8vyMPgCUAAcmAUFQGn8jADgCFkcNUeiITi8SNymQiHAdDCwjooABeLlUaxs-GjLzXYy3IayiC2eyOKAuNwkimBSoZLIMxkgTbJbZQdhcazNfXVLKIyXS1komxOHylWCYHbM53slQcLzy0QAAT4AiE6ygAGIoCgxQAGURK0oJdRigUFXUxeKJSWqaxeeGhcKh-ggQQiIwxuMARiTZigKdzums9LkLrKzaFnizINTSP2ql0%2BigsJDYfLEarsbFACZ6%2BYm%2B4tDXtLOClQa64eajmzXu1RZ9uO3yguudX5An3c7lISu10XRyWJxXI9X-PPkznlwUAFRUTkdwJH9e2-Ac72FOERzHL9QS7axf1oEA0EAk8u1AuD3FvHEtEgosyQ2LYdj7awkJQ%2BUgA)

```python
from typing import Any, Iterable, cast, overload

class Transform[X, Y]:
def transform(self, x: X, /) -> Y: ...

class Fork[X, TupleOut: tuple](Transform[X, TupleOut]):
r"""Apply multiple transforms to the same input and return a tuple of outputs."""

def __init__[U, V](
self: "Fork[U, tuple[V, ...]]", transforms: Iterable[Transform[U, V]], /
) -> None:
self.transforms: tuple[Transform[X, Any], ...] = tuple(transforms)

def transform(self, x: X, /) -> TupleOut:
return cast("TupleOut", tuple(t.transform(x) for t in self.transforms))

@overload # n=0
def fork[X=Any]() -> Fork[X, tuple[()]]: ...
@overload # n=1
def fork[X, Y](e: Transform[X, Y], /) -> Fork[X, tuple[Y]]: ...
@overload # n=2
def fork[X, Y1, Y2](e1: Transform[X, Y1], e2: Transform[X, Y2], /) -> Fork[X, tuple[Y1, Y2]]: ...
@overload # n>2
def fork[X, Y](*es: Transform[X, Y]) -> Fork[X, tuple[Y, ...]]: ...
def fork[X, Y](*args: Transform[X, Y]) -> Fork[X, tuple[Y, ...]]:
return Fork(args)

```

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the overload-checking error in the linked pyright playground using the provided Python sample, then trace how the third overload is compared with the implementation signature. Done means the valid example no longer reports InconsistentOverload while the overload consistency checks still handle the other overloads correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.