From 681e54cfe1642adddc41c4ff11198b8bc955d5af Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 1 Nov 2023 10:44:58 +0000 Subject: [PATCH] Fix crash on unpack call special-casing (#16381) Fixes https://github.com/python/mypy/issues/16380 Fix is quite straightforward, what was an `assert` really needs to be an `if`. --------- Co-authored-by: Jelle Zijlstra --- mypy/checkexpr.py | 38 +++++++++++-------------- test-data/unit/check-typevar-tuple.test | 22 ++++++++++++++ 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0207c245b1f9..95700a52af02 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -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): diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index a51b535a873c..e85863f0ed04 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -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]