Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ def append_int(*args: *Ts) -> tuple[*Ts, int]:

return (*args, 1)

# TODO should be tuple[Literal[True], Literal["a"], int]
reveal_type(append_int(True, "a")) # revealed: tuple[*tuple[Unknown, ...], int]
# TODO should be tuple[int]
reveal_type(append_int()) # revealed: tuple[*tuple[Unknown, ...], int]
reveal_type(append_int(True, "a")) # revealed: tuple[Literal[True], Literal["a"], int]
reveal_type(append_int()) # revealed: tuple[int]

def first_arg_int(*args: *tuple[int, *tuple[str, ...]]): ...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,12 @@ class Variadic(Generic[*Ts]):
reveal_type(Positional(())) # revealed: Positional[()]
reveal_type(Positional((1, "a"))) # revealed: Positional[int, str]

# TODO: Infer the `TypeVarTuple` from arguments matched to the variadic parameter.
reveal_type(Variadic()) # revealed: Variadic[*tuple[Unknown, ...]]
reveal_type(Variadic(1, "a")) # revealed: Variadic[*tuple[Unknown, ...]]
reveal_type(Variadic()) # revealed: Variadic[()]
reveal_type(Variadic(1, "a")) # revealed: Variadic[int, str]

def _(i: int, s: str) -> None:
reveal_type(Positional((i, s))) # revealed: Positional[int, str]
reveal_type(Variadic(i, s)) # revealed: Variadic[*tuple[Unknown, ...]]
reveal_type(Variadic(i, s)) # revealed: Variadic[int, str]
```

### Unspecified type arguments
Expand Down Expand Up @@ -445,6 +444,46 @@ class WithBackportedDefault(Generic[Unpack[Ts]]):
reveal_type(WithBackportedDefault().attr) # revealed: tuple[int, str]
```

## Generic Functions

### Starred variadic parameters

A legacy type-variable tuple is inferred from all positional arguments matched to `*args`, including
an empty argument list.

```py
from typing import TypeVarTuple, assert_type

Ts = TypeVarTuple("Ts")

def args_to_tuple(*args: *Ts) -> tuple[*Ts]:
raise NotImplementedError

def _(i: int, s: str) -> None:
assert_type(args_to_tuple(), tuple[()])
assert_type(args_to_tuple(i, s), tuple[int, str])
```

### Starred variadic parameters with fixed suffixes

Required tuple elements following the type-variable tuple are excluded from its inferred
specialization. The type-variable tuple can still be empty.

```py
from typing import TypeVarTuple, assert_type

Ts = TypeVarTuple("Ts")

class Env: ...

def exec_le(path: str, *args: *tuple[*Ts, Env], env: Env | None = None) -> tuple[*Ts]:
raise NotImplementedError

def _(i: int, s: str) -> None:
assert_type(exec_le("", Env()), tuple[()])
assert_type(exec_le(s, i, s, Env()), tuple[int, str])
```

## Type Aliases

### Legacy generic aliases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ def collect(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
reveal_type(args) # revealed: tuple[*Ts@collect]
raise NotImplementedError

# TODO: Infer the `TypeVarTuple` from arguments matched to the variadic parameter.
reveal_type(collect()) # revealed: tuple[Unknown, ...]
reveal_type(collect(1, "a")) # revealed: tuple[Unknown, ...]
reveal_type(collect()) # revealed: tuple[()]
reveal_type(collect(1, "a")) # revealed: tuple[Literal[1], Literal["a"]]
```

## Callable parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,12 @@ class Variadic[*Ts]:
reveal_type(Positional(())) # revealed: Positional[()]
reveal_type(Positional((1, "a"))) # revealed: Positional[int, str]

# TODO: Infer the `TypeVarTuple` from arguments matched to the variadic parameter.
reveal_type(Variadic()) # revealed: Variadic[*tuple[Unknown, ...]]
reveal_type(Variadic(1, "a")) # revealed: Variadic[*tuple[Unknown, ...]]
reveal_type(Variadic()) # revealed: Variadic[()]
reveal_type(Variadic(1, "a")) # revealed: Variadic[int, str]

def _(i: int, s: str) -> None:
reveal_type(Positional((i, s))) # revealed: Positional[int, str]
reveal_type(Variadic(i, s)) # revealed: Variadic[*tuple[Unknown, ...]]
reveal_type(Variadic(i, s)) # revealed: Variadic[int, str]
```

### Unspecified type arguments
Expand Down Expand Up @@ -301,8 +300,9 @@ def materialized_default[*Ts = *tuple[Any, ...]]() -> None:

### Starred variadic parameters

An unpacked `TypeVarTuple` can annotate `*args`. Inferring the `TypeVarTuple` from arguments matched
to the variadic parameter is not yet supported, so these calls use a gradual specialization.
An unpacked `TypeVarTuple` can annotate `*args`. Arguments matched to the variadic parameter infer
the complete type-variable tuple, preserving the shape of forwarded tuples and excluding ordinary
positional and keyword-only parameters.

```py
def simple[*Ts](*args: *Ts) -> tuple[*Ts]:
Expand All @@ -316,29 +316,61 @@ def with_kw_only[T, *Ts](*args: *Ts, kw: T) -> tuple[*Ts, T]:
raise NotImplementedError

def f(i: int, s: str, b: bool, t: tuple[int, str], vt: tuple[int, ...]) -> None:
reveal_type(simple()) # revealed: tuple[Unknown, ...]
reveal_type(simple(i, s)) # revealed: tuple[Unknown, ...]
reveal_type(simple(*(i, s))) # revealed: tuple[Unknown, ...]
reveal_type(simple(t)) # revealed: tuple[Unknown, ...]
reveal_type(simple(*t)) # revealed: tuple[Unknown, ...]
reveal_type(simple(*vt)) # revealed: tuple[Unknown, ...]

reveal_type(with_prefix(i)) # revealed: tuple[int, *tuple[Unknown, ...]]
reveal_type(with_prefix(i, s, b)) # revealed: tuple[int, *tuple[Unknown, ...]]
reveal_type(with_prefix(*t)) # revealed: tuple[int, *tuple[Unknown, ...]]
reveal_type(with_prefix(i, *t)) # revealed: tuple[int, *tuple[Unknown, ...]]
reveal_type(with_prefix(*vt)) # revealed: tuple[int, *tuple[Unknown, ...]]
reveal_type(with_prefix(i, *vt)) # revealed: tuple[int, *tuple[Unknown, ...]]

reveal_type(with_kw_only(kw=b)) # revealed: tuple[*tuple[Unknown, ...], bool]
reveal_type(with_kw_only(i, s, kw=b)) # revealed: tuple[*tuple[Unknown, ...], bool]
reveal_type(with_kw_only(t, kw=b)) # revealed: tuple[*tuple[Unknown, ...], bool]
reveal_type(with_kw_only(*t, kw=b)) # revealed: tuple[*tuple[Unknown, ...], bool]
reveal_type(with_kw_only(vt, kw=b)) # revealed: tuple[*tuple[Unknown, ...], bool]
reveal_type(with_kw_only(*vt, kw=b)) # revealed: tuple[*tuple[Unknown, ...], bool]
reveal_type(simple()) # revealed: tuple[()]
reveal_type(simple(i, s)) # revealed: tuple[int, str]
reveal_type(simple(*(i, s))) # revealed: tuple[int, str]
reveal_type(simple(t)) # revealed: tuple[tuple[int, str]]
reveal_type(simple(*t)) # revealed: tuple[int, str]
reveal_type(simple(*vt)) # revealed: tuple[int, ...]

reveal_type(with_prefix(i)) # revealed: tuple[int]
reveal_type(with_prefix(i, s, b)) # revealed: tuple[int, str, bool]
reveal_type(with_prefix(*t)) # revealed: tuple[int, str]
reveal_type(with_prefix(i, *t)) # revealed: tuple[int, int, str]
reveal_type(with_prefix(*vt)) # revealed: tuple[int, *tuple[int, ...]]
reveal_type(with_prefix(i, *vt)) # revealed: tuple[int, *tuple[int, ...]]

reveal_type(with_kw_only(kw=b)) # revealed: tuple[bool]
reveal_type(with_kw_only(i, s, kw=b)) # revealed: tuple[int, str, bool]
reveal_type(with_kw_only(t, kw=b)) # revealed: tuple[tuple[int, str], bool]
reveal_type(with_kw_only(*t, kw=b)) # revealed: tuple[int, str, bool]
reveal_type(with_kw_only(vt, kw=b)) # revealed: tuple[tuple[int, ...], bool]
reveal_type(with_kw_only(*vt, kw=b)) # revealed: tuple[*tuple[int, ...], bool]

# error: [missing-argument] "No argument provided for required parameter `kw` of function `with_kw_only`"
reveal_type(with_kw_only(i, s, b)) # revealed: tuple[*tuple[Unknown, ...], Unknown]
reveal_type(with_kw_only(i, s, b)) # revealed: tuple[int, str, bool, Unknown]
```

Variadic inference must preserve contextual argument types, including contexts that contain an outer
type variable.

```py
from typing import TypedDict

class Payload(TypedDict):
value: int

def contextual[T](value: T) -> None:
concrete: tuple[Payload, list[int]] = simple({"value": 1}, [])
generic: tuple[Payload, T] = simple({"value": 1}, value)
```

Fixed elements beside the type-variable tuple retain their individual bound diagnostics without
reporting the same error twice.

```py
def bounded_suffix[T: str, *Ts](*args: *tuple[*Ts, T]) -> tuple[*Ts, T]:
raise NotImplementedError

def bounded_arguments[U: bytes, T: str, *Ts](first: U, *args: *tuple[*Ts, T]) -> tuple[*Ts, T]:
raise NotImplementedError

bounded_suffix("ok", 1) # error: [invalid-argument-type] "upper bound `str`"
bounded_arguments(
1, # error: [invalid-argument-type] "upper bound `bytes`"
"ok",
2, # error: [invalid-argument-type] "upper bound `str`"
)
```

### Callable inference
Expand Down Expand Up @@ -793,8 +825,7 @@ accept_str_in_between(True, "phase", "status", b"ok")
accept_str_in_between(True, b"ok")
accept_str_in_between(True, 1, b"bad") # error: [invalid-argument-type]

# TODO: Infer the `TypeVarTuple` from arguments matched to the variadic parameter.
reveal_type(remove_bytes(1, "record", b"sum")) # revealed: tuple[Unknown, ...]
reveal_type(remove_bytes(1, "record", b"sum")) # revealed: tuple[Literal[1], Literal["record"]]
```

## `@staticmethod` and `@classmethod`
Expand Down
Loading
Loading