microsoft / pyright

Static Type Checker for Python
Other
13.04k stars 1.39k forks source link

Add TypeVarTuple support for pattern matching with "*" #8357

Closed minmax closed 1 month ago

minmax commented 1 month ago

pyright 1.1.371

from typing import TypeVar, TypeVarTuple, reveal_type

T = TypeVar("T")
TT = TypeVarTuple("TT")

def do(tup: tuple[T, *TT]) -> tuple[T, *TT]:
    match tup:
        case (first, *last_n):
            reveal_type(last_n)  # Type of "last_n" is "list[*TT@do]"

            return (first, *last_n) # Expression of type "tuple[T@do, *tuple[object, ...]]" is incompatible with return type "tuple[T@do, *TT@do]"
            # "tuple[T@do, *tuple[object, ...]]" is incompatible with "tuple[T@do, *TT@do]"
            # Tuple entry 2 is incorrect type
            # Type "*tuple[object, ...]" is incompatible with type "*TT@do" (reportReturnType)

Type list[*TT@do] of last_n variable looks weird to me, because of usage TypeVarTuple + list.

Also maybe is a way to "fix" error on return? But it looks like nothing better than tuple[T@do, *tuple[object, ...]] can be offered.

erictraut commented 1 month ago

Thanks for reporting. Changing tag from "enhancement request" to "bug" since I consider this a bug.

Pattern matching converts a sequence pattern * entry into a list, not a tuple. That means all size and order information is lost in the conversion, since list doesn't retain element size or order. I think the best that can be done here is to convert the TypeVarTuple to an Unknown, so last_n would take on the type list[Unknown] in this case. I think object (as you suggested) would be problematic because this type is going to be wrapped in a list, and the type parameter for list is invariant.

This should also resolve the error on return because the return expression will evaluate to tuple[T, *tuple[Unknown, ...]] which is compatible with tuple[T, *TT].

This will be addressed in the next release.

erictraut commented 1 month ago

This is addressed in pyright 1.1.372.