Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash on unpack call special-casing #16381

Merged
merged 2 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 16 additions & 22 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2440,34 +2440,28 @@ def check_argument_types(
# the suffices to the tuple, e.g. a single actual like
# Tuple[Unpack[Ts], int]
expanded_tuple = False
actual_kinds = [arg_kinds[a] for a in actuals]
if len(actuals) > 1:
first_actual_arg_type = get_proper_type(arg_types[actuals[0]])
p_actual_type = get_proper_type(arg_types[actuals[0]])
if (
isinstance(first_actual_arg_type, TupleType)
and len(first_actual_arg_type.items) == 1
and isinstance(first_actual_arg_type.items[0], UnpackType)
isinstance(p_actual_type, TupleType)
and len(p_actual_type.items) == 1
and isinstance(p_actual_type.items[0], UnpackType)
and actual_kinds == [nodes.ARG_STAR] + [nodes.ARG_POS] * (len(actuals) - 1)
):
# TODO: use walrus operator
actual_types = [first_actual_arg_type.items[0]] + [
arg_types[a] for a in actuals[1:]
]
actual_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * (len(actuals) - 1)

# If we got here, the callee was previously inferred to have a suffix.
assert isinstance(orig_callee_arg_type, UnpackType)
assert isinstance(orig_callee_arg_type.type, ProperType) and isinstance(
orig_callee_arg_type.type, TupleType
)
assert orig_callee_arg_type.type.items
callee_arg_types = orig_callee_arg_type.type.items
callee_arg_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * (
len(orig_callee_arg_type.type.items) - 1
)
expanded_tuple = True
actual_types = [p_actual_type.items[0]] + [arg_types[a] for a in actuals[1:]]
if isinstance(orig_callee_arg_type, UnpackType):
p_callee_type = get_proper_type(orig_callee_arg_type.type)
if isinstance(p_callee_type, TupleType):
assert p_callee_type.items
callee_arg_types = p_callee_type.items
callee_arg_kinds = [nodes.ARG_STAR] + [nodes.ARG_POS] * (
len(p_callee_type.items) - 1
)
expanded_tuple = True

if not expanded_tuple:
actual_types = [arg_types[a] for a in actuals]
actual_kinds = [arg_kinds[a] for a in actuals]
if isinstance(orig_callee_arg_type, UnpackType):
unpacked_type = get_proper_type(orig_callee_arg_type.type)
if isinstance(unpacked_type, TupleType):
Expand Down
22 changes: 22 additions & 0 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -2185,3 +2185,25 @@ def test2(
# E: Missing named argument "b"
return func(*args, **kwargs)
[builtins fixtures/tuple.pyi]

[case testUnpackTupleSpecialCaseNoCrash]
from typing import Tuple, TypeVar
from typing_extensions import Unpack

T = TypeVar("T")

def foo(*x: object) -> None: ...
def bar(*x: int) -> None: ...
def baz(*x: T) -> T: ...

keys: Tuple[Unpack[Tuple[int, ...]]]

foo(keys, 1)
foo(*keys, 1)

bar(keys, 1) # E: Argument 1 to "bar" has incompatible type "Tuple[Unpack[Tuple[int, ...]]]"; expected "int"
bar(*keys, 1) # OK

reveal_type(baz(keys, 1)) # N: Revealed type is "builtins.object"
reveal_type(baz(*keys, 1)) # N: Revealed type is "builtins.int"
[builtins fixtures/tuple.pyi]