Repository: python/mypy

Issue:
mypy crashes when passing multiple variadic tuples
```python
class A[*Ts]: ...

type x0 = tuple[int, ...]
type x1 = tuple[int, *tuple[int, ...]]

A[*x0, *x0]  # E: More than one Unpack in a type is not allowed
A[*x1, *x1]  # crash
```

https://mypy-play.net/?mypy=master&python=3.12&gist=f6165c82cda9047dbe8aa7ab5c746eda


Discussion:

Patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 77e6b0c005e2..fa5d9fdc82c4 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -295,6 +295,7 @@
     UnboundType,
     UnionType,
     UnpackType,
+    flatten_nested_tuples,
     get_proper_type,
     get_proper_types,
     has_type_vars,
@@ -6093,7 +6094,9 @@ def analyze_type_application_args(self, expr: IndexExpr) -> list[Type] | None:
             types.append(analyzed)
 
         if allow_unpack:
-            types = self.type_analyzer().check_unpacks_in_list(types)
+            # need to flatten away harmless unpacks like Unpack[tuple[int]]
+            flattened_items = flatten_nested_tuples(types)
+            types = self.type_analyzer().check_unpacks_in_list(flattened_items)
         if has_param_spec and num_args == 1 and types:
             first_arg = get_proper_type(types[0])
             single_any = len(types) == 1 and isinstance(first_arg, AnyType)
diff --git a/mypy/typeanal.py b/mypy/typeanal.py
index 204d3061c734..d44b13880cbb 100644
--- a/mypy/typeanal.py
+++ b/mypy/typeanal.py
@@ -1978,7 +1978,7 @@ def check_unpacks_in_list(self, items: list[Type]) -> list[Type]:
 
         if num_unpacks > 1:
             assert final_unpack is not None
-            self.fail("More than one Unpack in a type is not allowed", final_unpack.type)
+            self.fail("More than one variadic Unpack in a type is not allowed", final_unpack.type)
         return new_items
 
     def tuple_type(self, items: list[Type], line: int, column: int) -> TupleType:


Test patch:
diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test
index 817184dc561c..01364bdfa32a 100644
--- a/test-data/unit/check-python312.test
+++ b/test-data/unit/check-python312.test
@@ -2038,12 +2038,13 @@ class Z: ...  # E: Name "Z" already defined on line 2
 # https://github.com/python/mypy/issues/18856
 class A[*Ts]: ...
 
-A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
-a: A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
-def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ...  # E: More than one Unpack in a type is not allowed
+A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
+A[*tuple[*tuple[int, ...]], *tuple[*tuple[int, ...]]]  # E: More than one variadic Unpack in a type is not allowed
+a: A[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
+def foo(a: A[*tuple[int, ...], *tuple[int, ...]]): ...  # E: More than one variadic Unpack in a type is not allowed
 
-tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
-b: tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one Unpack in a type is not allowed
+tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
+b: tuple[*tuple[int, ...], *tuple[int, ...]]  # E: More than one variadic Unpack in a type is not allowed
 [builtins fixtures/tuple.pyi]
 [typing fixtures/typing-full.pyi]
 
diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test
index 2de2e45f0a96..c668f14eaa50 100644
--- a/test-data/unit/check-typevar-tuple.test
+++ b/test-data/unit/check-typevar-tuple.test
@@ -123,7 +123,7 @@ reveal_type(empty)  # N: Revealed type is "__main__.Variadic[()]"
 omitted: Variadic
 reveal_type(omitted)  # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[Any, ...]]]"
 
-bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]]  # E: More than one Unpack in a type is not allowed
+bad: Variadic[Unpack[Tuple[int, ...]], str, Unpack[Tuple[bool, ...]]]  # E: More than one variadic Unpack in a type is not allowed
 reveal_type(bad)  # N: Revealed type is "__main__.Variadic[Unpack[builtins.tuple[builtins.int, ...]], builtins.str]"
 
 bad2: Unpack[Tuple[int, ...]]  # E: Unpack is only valid in a variadic position
@@ -353,12 +353,12 @@ expect_variadic_array_2(u)
 Ts = TypeVarTuple("Ts")
 Ts2 = TypeVarTuple("Ts2")
 
-def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one Unpack in a type is not allowed
+def bad(x: Tuple[int, Unpack[Ts], str, Unpack[Ts2]]) -> None: # E: More than one variadic Unpack in a type is not allowed
 
     ...
 reveal_type(bad)  # N: Revealed type is "def [Ts, Ts2] (x: tuple[builtins.int, Unpack[Ts`-1], builtins.str])"
 
-def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None:  # E: More than one Unpack in a type is not allowed
+def bad2(x: Tuple[int, Unpack[Tuple[int, ...]], str, Unpack[Tuple[str, ...]]]) -> None:  # E: More than one variadic Unpack in a type is not allowed
     ...
 reveal_type(bad2)  # N: Revealed type is "def (x: tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]], builtins.str])"
 [builtins fixtures/tuple.pyi]
@@ -571,7 +571,7 @@ from typing_extensions import Unpack, TypeVarTuple
 
 Ts = TypeVarTuple("Ts")
 Us = TypeVarTuple("Us")
-a: Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one Unpack in a type is not allowed
+a: Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one variadic Unpack in a type is not allowed
 reveal_type(a)  # N: Revealed type is "def [Ts, Us] (*Unpack[Ts`-1]) -> builtins.int"
 b: Callable[[Unpack], int]  # E: Unpack[...] requires exactly one type argument
 reveal_type(b)  # N: Revealed type is "def (*Any) -> builtins.int"
@@ -725,15 +725,15 @@ Ts = TypeVarTuple("Ts")
 Us = TypeVarTuple("Us")
 class G(Generic[Unpack[Ts]]): ...
 
-A = Tuple[Unpack[Ts], Unpack[Us]]  # E: More than one Unpack in a type is not allowed
+A = Tuple[Unpack[Ts], Unpack[Us]]  # E: More than one variadic Unpack in a type is not allowed
 x: A[int, str]
 reveal_type(x)  # N: Revealed type is "tuple[builtins.int, builtins.str]"
 
-B = Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one Unpack in a type is not allowed
+B = Callable[[Unpack[Ts], Unpack[Us]], int]  # E: More than one variadic Unpack in a type is not allowed
 y: B[int, str]
 reveal_type(y)  # N: Revealed type is "def (builtins.int, builtins.str) -> builtins.int"
 
-C = G[Unpack[Ts], Unpack[Us]]  # E: More than one Unpack in a type is not allowed
+C = G[Unpack[Ts], Unpack[Us]]  # E: More than one variadic Unpack in a type is not allowed
 z: C[int, str]
 reveal_type(z)  # N: Revealed type is "__main__.G[builtins.int, builtins.str]"
 [builtins fixtures/tuple.pyi]
@@ -2223,12 +2223,12 @@ cb2(1, 2, 3, a="a", b="b")
 cb2(1, a="a", b="b")  # E: Too few arguments
 cb2(1, 2, 3, a="a")  # E: Missing named argument "b"
 
-bad1: Callable[[Unpack[Ints], Unpack[Ints]], None]  # E: More than one Unpack in a type is not allowed
+bad1: Callable[[Unpack[Ints], Unpack[Ints]], None]  # E: More than one variadic Unpack in a type is not allowed
 reveal_type(bad1)  # N: Revealed type is "def (*builtins.int)"
 bad2: Callable[[Unpack[Keywords], Unpack[Keywords]], None]  # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple)
 reveal_type(bad2)  # N: Revealed type is "def (*Any, **Unpack[TypedDict('__main__.Keywords', {'a': builtins.str, 'b': builtins.str})])"
 bad3: Callable[[Unpack[Keywords], Unpack[Ints]], None]  # E: "Keywords" cannot be unpacked (must be tuple or TypeVarTuple) \
-                                                        # E: More than one Unpack in a type is not allowed
+                                                        # E: More than one variadic Unpack in a type is not allowed
 reveal_type(bad3)  # N: Revealed type is "def (*Any)"
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-typeddict.pyi]