Unpacked iterables are not typed correctly
- Dominant language
- Rust
- Stars
- 7k
- Forks
- 516
- PR merge metrics
- No merged PRs in 30d
Description
### Describe the Bug
I'm trying to pop the first element in a `tuple[T]` in a type-safe way, i.e.:
```py
from typing import assert_type
from typing import reveal_type
from typing import TypeVar, TypeAlias, overload
T = TypeVar("T")
TupleOrValue: TypeAlias = tuple[T, ...] | T
@overload
def _split_tuple_or_value(tuple_or_value: None) -> tuple[None, None]:
"""No value."""
...
@overload
def _split_tuple_or_value(tuple_or_value: T | tuple[T]) -> tuple[T, None]:
"""Single value."""
...
@overload
def _split_tuple_or_value(tuple_or_value: tuple[T, T]) -> tuple[T, T]:
"""Pair of values."""
...
@overload
def _split_tuple_or_value(
tuple_or_value: tuple[T, ...],
) -> tuple[T, tuple[T, ...]]:
"""More than two values."""
...
def _split_tuple_or_value(
tuple_or_value: TupleOrValue[T],
) -> tuple[T, TupleOrValue[T] | None]:
"""Split the given `TupleOrValue[T]` into its head and tail.
E.g. in the following cases:
* `a, b = _split_tuple_or_value((1, 2, 3))`: `a=1`, `b=(2, 3)`,
* `a, b = _split_tuple_or_value((1, 2))`: `a=1`, `b=2`,
* `a, b = _split_tuple_or_value((1,))`: `a=1`, `b=None`,
* `a, b = _split_tuple_or_value(1)`: `a=1`, `b=None`,
Args:
tuple_or_value: The `TupleOrValue[T]` to split.
Returns:
A `tuple` consisting of the first element of the input and the remaining
elements as a `TupleOrValue[T]`.
"""
if not isinstance(tuple_or_value, tuple):
head, tail = tuple_or_value, None
else:
head, *tail = tuple_or_value
tail = tuple(tail)
if len(tail) < 2:
tail, *_ = tail + (None,)
assert_type(head, T)
assert_type(tail, TupleOrValue[T] |None)
return head, tail
def test(tov: TupleOrValue[int]):
head, tail = _split_tuple_or_value(tov)
assert_type(head, int)
assert_type(tail, TupleOrValue[int])
```
The last two `assert_type` statements fail with:
```
ERROR sandbox.py:66:16-27: assert_type(int | tuple[int, ...], int) failed [assert-type]
ERROR sandbox.py:67:16-41: assert_type(None, int | tuple[int, ...]) failed [assert-type]
```
which is odd, since:
* All `@overload`s have the first return value as type `T`,
* The `assert_type` statements at the end of `_split_tuple_or_value` don't fail.
### Sandbox Link
https://pyrefly.org/sandbox/?project=N4IgZglgNgpgziAXKOBDAdgEwEYHsAeAdAA4CeS4ATrgLYAEALqcROgOZ0Q3G6UN2o4cGHwD6TYjAA66MNXoTWHLjz51KMAG4xUUcc2mz5jZks7de-ACoGAaqkoAaOjckBBKBEHPc2ylFxUTBkZKzoAXhc7BwAKKRAreIBKUIBXYlgAeUp7KFSYRCj3T0EIxnTYAG0rZ0I6gF06AB8XEPQZAAFfEQCgmUwYMDpROAyIBnEKmFFeUU1dfJiGKZnKOYWCugA5XHQYJLoAWgA%2BcoyYSp295yuYesQZOjp4l5Adunm8mEJXl-Qnuo-drArp%2BXrBLCDYajTwTZbnVbrL5LFazT75QphFrwqpWeoHE5nXE3XZ3B7-Z4gV4AZSUsA%2BGx%2BVOZjzogLanW6-kCEIGQxGYzhqLW6JgKIRaI2hRxFxqLnxR1OMuqzjx5KevxAAAVUBBKHRcENRXAmb8KezgZywTz%2BlCBbDJhKRRs4hSnjLEaLpVMVWyGo4ZASlT65cq5YD6vdWZqALK8GCMAAWGEYAHdcAyvibNayLW0%2BdDBY7YJ6Xaz3cKkRiXFNsrl8tV6gH0EGibLVbWchtG81tqSoxTNdTBUmE2wINp-gADKyd%2Buy%2BpTzjoBgZ8ZwOiJnSYARYRi6qBA1kAUUIbEIy9HdDAuCgAVTZgAxoJ4Oq6AAqOhT1DObBle3jMW0ySsiMQAIzOAATM4ADMSRJFOhTfuEYFTs4U7YOEMTQXQcFoayn7fr%2B-4woBHogYs4FQfBiFfqgKFoV%2BmGQfhFKET%2BdB-pEAFCk6VZilRNFIfRqHoZhtysU87HEdxpG8SWFFimBCHCQxYnhBJzasm4lBsHAb4VnxXouFuX6zucdbdniS6rnQclHhSABKMDLJQ6D6eWdBuF%2BMpLo%2BuxwBAcAMGYhpXpAlDBXQMCwDQMArgaQwMKZrDEKk-AYDuyUJhoNC6ugSieTFMBxSuG6lKgZlzlZi5AhqLJUhSEBDOguD8EFrDBRgj5iuRzpfM4MpJAZm7boNB5lH1-EknsrIxcII1bkEzjvgwE2RFNoqeWt0CTVMSwHikbqcEMsDoAd0AHAAPHQkEje6B4raIk0TQA1HQMS3I4R1PIIwhiBIYpLZgqo-QIQgiHCBgXVAHYWV2Xw9i0txgxorn-MD43QPmUIMPADBLL4mLVYjrAMPib6Y-uu2yUWm0uqumhg39kP6JIMRU2TzMQwD0M7bDNbw-OlRk-iICOCAZAaGAUCkIQq40FAFAAMR0FqpDS7LdmZXg%2BB0P5sgQGwqSUKgIW7ECqvUjACaJgwDDEPpAD0TtS4MsuELwbBO-FTuYLgj5wE7BuQMbpvm%2BgTvXrwAjzNAqDYPSIdGybZsQLsBrEBH2boGQyW7IcfiBRnkTxDBhBgZBfyVCI1CRfUMipAVFh8DAmCHJgeowI%2BIXaGUADkRutRo-cyK1DCHBoACOqRd%2B3ADWMCkIcqCPj1QgD6mDjoKP6AgAAvhLq%2B9zAABi0AwBQaBYLrJDkPvQA
### (Only applicable for extension issues) IDE Information
_No response_
Contributor guide
Research direction
Start by reproducing the reported failure from the linked Pyrefly sandbox and inspect how the overload call is inferred for TupleOrValue[int]. Trace the relevant type-checking behavior, then verify that the final assert_type statements infer int and TupleOrValue[int] while the existing assertions continue to pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust
- Domain
- compilers, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100