Skip to content

Commit

Permalink
Fix crash on unpack call special-casing (#16381)
Browse files Browse the repository at this point in the history
Fixes #16380

Fix is quite straightforward, what was an `assert` really needs to be an
`if`.

---------

Co-authored-by: Jelle Zijlstra <[email protected]>
  • Loading branch information
2 people authored and JukkaL committed Nov 1, 2023
1 parent f68f463 commit 681e54c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 22 deletions.
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]

0 comments on commit 681e54c

Please sign in to comment.