Skip to content

(🐞) Protocol compatibility of Unpacked TypeVarTuple is broken when signature is not a trivial *args: Any #16567

@KotlinIsland

Description

@KotlinIsland
# mypy: disable-error-code=empty-body
from typing import Any, Protocol, Unpack, TypeVarTuple

Ts = TypeVarTuple("Ts")


class A1(Protocol[Unpack[Ts]]):
    def f(self, *args: Unpack[Ts]) -> None: ...

class B1:
    def f(self, x: str) -> None: ...

def f1(x: A1[Unpack[Ts]]) -> None: ...

f1(B1())

class A2(Protocol[Unpack[Ts]]):
    def f(self, z: str, *args: Unpack[Ts]) -> None: ...

class B2(A2[str]):
    def f(self, z: str, x: str) -> None: ...

class C2:
    def f(self, z: str, x: str) -> None: ...


def f2(x: A2[Unpack[Ts]]) -> None: ...

f2(B2())
f2(C2())
# error: Argument 1 to "f2" has incompatible type "C2"; expected "A2[*tuple[Never, ...]]"  [arg-type]
# note: Following member(s) of "C2" have conflicts:
# note:     Expected:
# note:         def f(self, z: str, *args: Never) -> None
# note:     Got:
# note:         def f(self, z: str, x: str) -> None

I believe this is caused by:

mypy/mypy/subtypes.py

Lines 1597 to 1607 in 1200d1d

if (
right.arg_kinds == [ARG_STAR]
and isinstance(get_proper_type(right.arg_types[0]), AnyType)
and not is_proper_subtype
):
# Similar to how (*Any, **Any) is considered a supertype of all callables, we consider
# (*Any) a supertype of all callables with positional arguments. This is needed in
# particular because we often refuse to try type inference if actual type is not
# a subtype of erased template type.
if all(k.is_positional() for k in left.arg_kinds) and ignore_pos_arg_names:
return True

@ilevkivskyi

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-pep-646PEP 646 (TypeVarTuple, Unpack)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions