Skip to content

Commit 66bfe8c

Browse files
authored
Fix crash involving Unpack-ed TypeVarTuple (#20323)
Fixes #20093 This fixes the crash, but not the false positive (the false positive existed prior to the regression that introduced the crash)
1 parent 9c1406c commit 66bfe8c

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

mypy/typeops.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ def erase_to_bound(t: Type) -> Type:
508508
def callable_corresponding_argument(
509509
typ: NormalizedCallableType | Parameters, model: FormalArgument
510510
) -> FormalArgument | None:
511-
"""Return the argument a function that corresponds to `model`"""
511+
"""Return the argument of a function that corresponds to `model`"""
512512

513513
by_name = typ.argument_by_name(model.name)
514514
by_pos = typ.argument_by_position(model.pos)
@@ -522,17 +522,23 @@ def callable_corresponding_argument(
522522
# taking both *args and **args, or a pair of functions like so:
523523

524524
# def right(a: int = ...) -> None: ...
525-
# def left(__a: int = ..., *, a: int = ...) -> None: ...
525+
# def left(x: int = ..., /, *, a: int = ...) -> None: ...
526526
from mypy.meet import meet_types
527527

528528
if (
529529
not (by_name.required or by_pos.required)
530530
and by_pos.name is None
531531
and by_name.pos is None
532+
# This is not principled, but prevents a crash. It's weird to have a FormalArgument
533+
# that has an UnpackType.
534+
and not isinstance(by_name.typ, UnpackType)
535+
and not isinstance(by_pos.typ, UnpackType)
532536
):
533537
return FormalArgument(
534538
by_name.name, by_pos.pos, meet_types(by_name.typ, by_pos.typ), False
535539
)
540+
return by_name
541+
536542
return by_name if by_name is not None else by_pos
537543

538544

test-data/unit/check-overloading.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,19 @@ def foo(*args: int | str, **kw: int | Foo) -> None:
263263
pass
264264
[builtins fixtures/tuple.pyi]
265265

266+
267+
[case testTypeCheckOverloadImplOverlapVarArgsAndKwargsNever]
268+
from __future__ import annotations
269+
from typing import overload
270+
271+
@overload # E: Single overload definition, multiple required
272+
def foo(x: int) -> None: ...
273+
274+
def foo(*args: int, **kw: str) -> None: # E: Overloaded function implementation does not accept all possible arguments of signature 1
275+
pass
276+
[builtins fixtures/tuple.pyi]
277+
278+
266279
[case testTypeCheckOverloadWithImplTooSpecificRetType]
267280
from typing import overload, Any
268281

test-data/unit/check-typevar-tuple.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,3 +2716,26 @@ class MyTuple(tuple[Unpack[Union[int, str]]], Generic[Unpack[Ts]]): # E: "Union
27162716
x: MyTuple[int, str]
27172717
reveal_type(x[0]) # N: Revealed type is "Any"
27182718
[builtins fixtures/tuple.pyi]
2719+
2720+
[case testHigherOrderFunctionUnpackTypeVarTupleViaParamSpec]
2721+
from typing import Callable, ParamSpec, TypeVar, TypeVarTuple, Unpack
2722+
2723+
P = ParamSpec("P")
2724+
T = TypeVar("T")
2725+
Ts = TypeVarTuple("Ts")
2726+
2727+
def call(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
2728+
return func(*args, **kwargs)
2729+
2730+
2731+
def run(func: Callable[[Unpack[Ts]], T], *args: Unpack[Ts], some_kwarg: str = "asdf") -> T:
2732+
raise
2733+
2734+
2735+
def foo() -> str:
2736+
return "hello"
2737+
2738+
2739+
# this is a false positive, but it no longer crashes
2740+
call(run, foo, some_kwarg="a") # E: Argument 1 to "call" has incompatible type "def [Ts`-1, T] run(func: def (*Unpack[Ts]) -> T, *args: Unpack[Ts], some_kwarg: str = ...) -> T"; expected "Callable[[Callable[[], str], str], str]"
2741+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)