mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[ty] Upcast heterogeneous and mixed tuples to homogeneous tuples where it's necessary to solve a TypeVar
(#19635)
## Summary This PR improves our generics solver such that we are able to solve the `TypeVar` in this snippet to `int | str` (the union of the elements in the heterogeneous tuple) by upcasting the heterogeneous tuple to its pure-homogeneous-tuple supertype: ```py def f[T](x: tuple[T, ...]) -> T: return x[0] def g(x: tuple[int, str]): reveal_type(f(x)) ``` ## Test Plan Mdtests. Some TODOs remain in the mdtest regarding solving `TypeVar`s for mixed tuples, but I think this PR on its own is a significant step forward for our generics solver when it comes to tuple types. --------- Co-authored-by: Douglas Creager <dcreager@dcreager.net>
This commit is contained in:
parent
d797592f70
commit
ec3d5ebda2
4 changed files with 72 additions and 29 deletions
|
@ -145,27 +145,34 @@ T = TypeVar("T")
|
|||
def takes_mixed_tuple_suffix(x: tuple[int, bytes, *tuple[str, ...], T, int]) -> T:
|
||||
return x[-2]
|
||||
|
||||
# TODO: revealed: Literal[True]
|
||||
reveal_type(takes_mixed_tuple_suffix((1, b"foo", "bar", "baz", True, 42))) # revealed: Unknown
|
||||
|
||||
def takes_mixed_tuple_prefix(x: tuple[int, T, *tuple[str, ...], bool, int]) -> T:
|
||||
return x[1]
|
||||
|
||||
# TODO: revealed: Literal[b"foo"]
|
||||
reveal_type(takes_mixed_tuple_prefix((1, b"foo", "bar", "baz", True, 42))) # revealed: Unknown
|
||||
def _(x: tuple[int, bytes, *tuple[str, ...], bool, int]):
|
||||
reveal_type(takes_mixed_tuple_suffix(x)) # revealed: bool
|
||||
reveal_type(takes_mixed_tuple_prefix(x)) # revealed: bytes
|
||||
|
||||
reveal_type(takes_mixed_tuple_suffix((1, b"foo", "bar", "baz", True, 42))) # revealed: Literal[True]
|
||||
reveal_type(takes_mixed_tuple_prefix((1, b"foo", "bar", "baz", True, 42))) # revealed: Literal[b"foo"]
|
||||
|
||||
def takes_fixed_tuple(x: tuple[T, int]) -> T:
|
||||
return x[0]
|
||||
|
||||
def _(x: tuple[str, int]):
|
||||
reveal_type(takes_fixed_tuple(x)) # revealed: str
|
||||
|
||||
reveal_type(takes_fixed_tuple((True, 42))) # revealed: Literal[True]
|
||||
|
||||
def takes_homogeneous_tuple(x: tuple[T, ...]) -> T:
|
||||
return x[0]
|
||||
|
||||
# TODO: revealed: Literal[42]
|
||||
reveal_type(takes_homogeneous_tuple((42,))) # revealed: Unknown
|
||||
# TODO: revealed: Literal[42, 43]
|
||||
reveal_type(takes_homogeneous_tuple((42, 43))) # revealed: Unknown
|
||||
def _(x: tuple[str, int], y: tuple[bool, ...], z: tuple[int, str, *tuple[range, ...], bytes]):
|
||||
reveal_type(takes_homogeneous_tuple(x)) # revealed: str | int
|
||||
reveal_type(takes_homogeneous_tuple(y)) # revealed: bool
|
||||
reveal_type(takes_homogeneous_tuple(z)) # revealed: int | str | range | bytes
|
||||
|
||||
reveal_type(takes_homogeneous_tuple((42,))) # revealed: Literal[42]
|
||||
reveal_type(takes_homogeneous_tuple((42, 43))) # revealed: Literal[42, 43]
|
||||
```
|
||||
|
||||
## Inferring a bound typevar
|
||||
|
|
|
@ -131,27 +131,34 @@ reveal_type(takes_in_protocol(ExplicitGenericSub[str]())) # revealed: str
|
|||
def takes_mixed_tuple_suffix[T](x: tuple[int, bytes, *tuple[str, ...], T, int]) -> T:
|
||||
return x[-2]
|
||||
|
||||
# TODO: revealed: Literal[True]
|
||||
reveal_type(takes_mixed_tuple_suffix((1, b"foo", "bar", "baz", True, 42))) # revealed: Unknown
|
||||
|
||||
def takes_mixed_tuple_prefix[T](x: tuple[int, T, *tuple[str, ...], bool, int]) -> T:
|
||||
return x[1]
|
||||
|
||||
# TODO: revealed: Literal[b"foo"]
|
||||
reveal_type(takes_mixed_tuple_prefix((1, b"foo", "bar", "baz", True, 42))) # revealed: Unknown
|
||||
def _(x: tuple[int, bytes, *tuple[str, ...], bool, int]):
|
||||
reveal_type(takes_mixed_tuple_suffix(x)) # revealed: bool
|
||||
reveal_type(takes_mixed_tuple_prefix(x)) # revealed: bytes
|
||||
|
||||
reveal_type(takes_mixed_tuple_suffix((1, b"foo", "bar", "baz", True, 42))) # revealed: Literal[True]
|
||||
reveal_type(takes_mixed_tuple_prefix((1, b"foo", "bar", "baz", True, 42))) # revealed: Literal[b"foo"]
|
||||
|
||||
def takes_fixed_tuple[T](x: tuple[T, int]) -> T:
|
||||
return x[0]
|
||||
|
||||
def _(x: tuple[str, int]):
|
||||
reveal_type(takes_fixed_tuple(x)) # revealed: str
|
||||
|
||||
reveal_type(takes_fixed_tuple((True, 42))) # revealed: Literal[True]
|
||||
|
||||
def takes_homogeneous_tuple[T](x: tuple[T, ...]) -> T:
|
||||
return x[0]
|
||||
|
||||
# TODO: revealed: Literal[42]
|
||||
reveal_type(takes_homogeneous_tuple((42,))) # revealed: Unknown
|
||||
# TODO: revealed: Literal[42, 43]
|
||||
reveal_type(takes_homogeneous_tuple((42, 43))) # revealed: Unknown
|
||||
def _(x: tuple[str, int], y: tuple[bool, ...], z: tuple[int, str, *tuple[range, ...], bytes]):
|
||||
reveal_type(takes_homogeneous_tuple(x)) # revealed: str | int
|
||||
reveal_type(takes_homogeneous_tuple(y)) # revealed: bool
|
||||
reveal_type(takes_homogeneous_tuple(z)) # revealed: int | str | range | bytes
|
||||
|
||||
reveal_type(takes_homogeneous_tuple((42,))) # revealed: Literal[42]
|
||||
reveal_type(takes_homogeneous_tuple((42, 43))) # revealed: Literal[42, 43]
|
||||
```
|
||||
|
||||
## Inferring a bound typevar
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue