# PEP 695 `ParamSpec`

`ParamSpec` was introduced in Python 3.12 while the support for specifying defaults was added in
Python 3.13.

```toml
[environment]
python-version = "3.13"
```

## Definition

```py
def foo1[**P]() -> None:
    reveal_type(P)  # revealed: ParamSpec
```

## Bounds and constraints

`ParamSpec`, when defined using the new syntax, does not allow defining bounds or constraints.

TODO: This results in a lot of syntax errors mainly because the AST doesn't accept them in this
position. The parser could do a better job in recovering from these errors.

<!-- fmt:off -->

```py
# error: [invalid-syntax]
# error: [invalid-syntax]
# error: [invalid-syntax]
# error: [invalid-syntax]
# error: [invalid-syntax]
# error: [invalid-syntax]
def foo[**P: int]() -> None:
    # error: [invalid-syntax]
    # error: [invalid-syntax]
    pass
```

<!-- fmt:on -->

## Default

The default value for a `ParamSpec` can be either a list of types, `...`, or another `ParamSpec`.

```py
def foo2[**P = ...]() -> None:
    reveal_type(P)  # revealed: ParamSpec

def foo3[**P = [int, str]]() -> None:
    reveal_type(P)  # revealed: ParamSpec

def foo4[**P, **Q = P]():
    reveal_type(P)  # revealed: ParamSpec
    reveal_type(Q)  # revealed: ParamSpec

# error: [invalid-type-form] "Bare ParamSpec `Q` is not valid in this context"
def foo5[**Q, **P = [Q]]() -> None:
    pass
```

Other values are invalid.

```py
# error: [invalid-paramspec]
def foo[**P = int]() -> None:
    pass
```

## Validating `ParamSpec` usage

`ParamSpec` is only valid as the first element to `Callable` or the final element to `Concatenate`.

<!-- snapshot-diagnostics -->

```py
from typing import Any, Final, ParamSpec, Callable, Concatenate, Union, Optional, Annotated

def valid[**P](
    a1: Callable[P, int],
    a2: Callable[Concatenate[int, P], int],
    a3: Callable["P", int],
    a4: Callable[Concatenate[int, "P"], int],
) -> None: ...
def invalid[**P](
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a1: P,
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a3: Callable[[P], int],
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a4: Callable[..., P],
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a5: Callable[Concatenate[P, ...], int],
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a6: P | int,
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a7: Union[P, int],
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a8: Optional[P],
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a9: Annotated[P, "metadata"],
) -> None: ...

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid_return[**P]() -> P:
    raise NotImplementedError

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
type Alias[**P] = P

def invalid_variable_annotation[**P](y: Any) -> None:
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    x: P = y

def invalid_with_qualifier[**P](y: Any) -> None:
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    x: Final[P] = y

# error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
def invalid_stringified_return[**P]() -> "P":
    raise NotImplementedError

def invalid_stringified_annotation[**P](
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    a: "P",
) -> None: ...
def invalid_stringified_variable_annotation[**P](y: Any) -> None:
    # error: [invalid-type-form] "Bare ParamSpec `P` is not valid in this context"
    x: "P" = y

class InvalidSpecializationTarget[**P]:
    attr: Callable[P, None]

def invalid_specialization[**Q](
    # error: [invalid-type-form] "Bare ParamSpec `Q` is not valid in this context"
    a: InvalidSpecializationTarget[[Q]],
    # error: [invalid-type-form] "Bare ParamSpec `Q` is not valid in this context"
    b: InvalidSpecializationTarget[Q,],
) -> None: ...
```

## Validating `P.args` and `P.kwargs` usage

The components of `ParamSpec` i.e., `P.args` and `P.kwargs` are only valid when used as the
annotated types of `*args` and `**kwargs` respectively.

```py
from typing import Callable

def foo[**P](c: Callable[P, int]) -> None:
    def nested1(*args: P.args, **kwargs: P.kwargs) -> None: ...

    # error: [invalid-type-form] "`P.kwargs` is valid only in `**kwargs` annotation: Did you mean `P.args`?"
    # error: [invalid-type-form] "`P.args` is valid only in `*args` annotation: Did you mean `P.kwargs`?"
    def nested2(*args: P.kwargs, **kwargs: P.args) -> None: ...

    # error: [invalid-paramspec] "`*args: P.args` must be accompanied by `**kwargs: P.kwargs`"
    def nested3(*args: P.args) -> None: ...

    # error: [invalid-paramspec] "`**kwargs: P.kwargs` must be accompanied by `*args: P.args`"
    def nested4(**kwargs: P.kwargs) -> None: ...

    # error: [invalid-paramspec] "No parameters may appear between `*args: P.args` and `**kwargs: P.kwargs`"
    def nested5(*args: P.args, x: int, **kwargs: P.kwargs) -> None: ...

    # error: [invalid-paramspec] "`P.args` is only valid for annotating `*args`"
    def nested6(x: P.args) -> None: ...
    def nested7(
        *args: P.args,
        # error: [invalid-paramspec] "`*args: P.args` must be accompanied by `**kwargs: P.kwargs`"
        **kwargs: int,
    ) -> None: ...
```

And, they need to be used together.

```py
def foo[**P](c: Callable[P, int]) -> None:
    # error: [invalid-paramspec] "`*args: P.args` must be accompanied by `**kwargs: P.kwargs`"
    def nested1(*args: P.args) -> None: ...

    # error: [invalid-paramspec] "`**kwargs: P.kwargs` must be accompanied by `*args: P.args`"
    def nested2(**kwargs: P.kwargs) -> None: ...

class Foo[**P]:
    # error: [invalid-paramspec] "`P.args` is only valid for annotating `*args` function parameters"
    args: P.args

    # error: [invalid-paramspec] "`P.kwargs` is only valid for annotating `**kwargs` function parameters"
    kwargs: P.kwargs
```

The name of these parameters does not need to be `args` or `kwargs`, it's the annotated type to the
respective variadic parameter that matters.

```py
class Foo3[**P]:
    def method1(self, *paramspec_args: P.args, **paramspec_kwargs: P.kwargs) -> None: ...
    def method2(
        self,
        # error: [invalid-type-form] "`P.kwargs` is valid only in `**kwargs` annotation: Did you mean `P.args`?"
        *paramspec_args: P.kwargs,
        # error: [invalid-type-form] "`P.args` is valid only in `*args` annotation: Did you mean `P.kwargs`?"
        **paramspec_kwargs: P.args,
    ) -> None: ...
```

Error messages for `invalid-paramspec` also use the actual parameter names:

```py
def bar[**P](c: Callable[P, int]) -> None:
    # error: [invalid-paramspec] "`*my_args: P.args` must be accompanied by `**my_kwargs: P.kwargs`"
    def f1(*my_args: P.args, **my_kwargs: int) -> None: ...

    # error: [invalid-paramspec] "`*positional: P.args` must be accompanied by `**kwargs: P.kwargs`"
    def f2(*positional: P.args) -> None: ...

    # error: [invalid-paramspec] "`**keyword: P.kwargs` must be accompanied by `*args: P.args`"
    def f3(**keyword: P.kwargs) -> None: ...

    # error: [invalid-paramspec] "No parameters may appear between `*a: P.args` and `**kw: P.kwargs`"
    def f4(*a: P.args, x: int, **kw: P.kwargs) -> None: ...
```

It isn't allowed to annotate an instance attribute either:

```py
class Foo4[**P]:
    def __init__(self, fn: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> None:
        self.fn = fn
        # error: [invalid-paramspec] "`P.args` is only valid for annotating `*args` function parameters"
        self.args: P.args = args
        # error: [invalid-paramspec] "`P.kwargs` is only valid for annotating `**kwargs` function parameters"
        self.kwargs: P.kwargs = kwargs
```

## Semantics of `P.args` and `P.kwargs`

The type of `args` and `kwargs` inside the function is `P.args` and `P.kwargs` respectively instead
of `tuple[P.args, ...]` and `dict[str, P.kwargs]`.

### Passing `*args` and `**kwargs` to a callable

```py
from typing import Callable

def f[**P](func: Callable[P, int]) -> Callable[P, None]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> None:
        reveal_type(args)  # revealed: P@f.args
        reveal_type(kwargs)  # revealed: P@f.kwargs
        reveal_type(func(*args, **kwargs))  # revealed: int

        # error: [invalid-argument-type] "Argument is incorrect: Expected `P@f.args`, found `P@f.kwargs`"
        # error: [invalid-argument-type] "Argument is incorrect: Expected `P@f.kwargs`, found `P@f.args`"
        reveal_type(func(*kwargs, **args))  # revealed: int

        # error: [invalid-argument-type] "Argument is incorrect: Expected `P@f.args`, found `P@f.kwargs`"
        # error: [missing-argument]
        reveal_type(func(args, kwargs))  # revealed: int

        # Both parameters are required
        # error: [missing-argument]
        reveal_type(func())  # revealed: int
        # error: [missing-argument]
        reveal_type(func(*args))  # revealed: int
        # error: [missing-argument]
        reveal_type(func(**kwargs))  # revealed: int
    return wrapper
```

### Operations on `P.args` and `P.kwargs`

The type of `P.args` and `P.kwargs` behave like a `tuple` and `dict` respectively. Internally, they
are represented as a type variable that has an upper bound of `tuple[object, ...]` and
`Top[dict[str, Any]]` respectively.

```py
from typing import Callable, Any

def f[**P](func: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> None:
    reveal_type(args + ("extra",))  # revealed: tuple[object, ...]
    reveal_type(args + (1, 2, 3))  # revealed: tuple[object, ...]
    reveal_type(args[0])  # revealed: object

    reveal_type("key" in kwargs)  # revealed: bool
    reveal_type(kwargs.get("key"))  # revealed: object
    reveal_type(kwargs["key"])  # revealed: object
```

## Specializing generic classes explicitly

```py
from typing import Any, Callable, ParamSpec

class OnlyParamSpec[**P1]:
    attr: Callable[P1, None]

class TwoParamSpec[**P1, **P2]:
    attr1: Callable[P1, None]
    attr2: Callable[P2, None]

class TypeVarAndParamSpec[T1, **P1]:
    attr: Callable[P1, T1]

class ParamSpecAndTypeVar[**P1, T1]:
    attr: Callable[P1, T1]
```

Explicit specialization of a generic class involving `ParamSpec` is done by providing either a list
of types, `...`, or another in-scope `ParamSpec`.

```py
reveal_type(OnlyParamSpec[[]]().attr)  # revealed: () -> None
reveal_type(OnlyParamSpec[[int, str]]().attr)  # revealed: (int, str, /) -> None
reveal_type(OnlyParamSpec[...]().attr)  # revealed: (...) -> None

def func[**P2](c: Callable[P2, None]):
    reveal_type(OnlyParamSpec[P2]().attr)  # revealed: (**P2@func) -> None

P2 = ParamSpec("P2")

# error: [invalid-type-arguments] "ParamSpec `P2` is unbound"
reveal_type(OnlyParamSpec[P2]().attr)  # revealed: (...) -> None

# error: [invalid-type-arguments] "No type argument provided for required type variable `P1` of class `OnlyParamSpec`"
reveal_type(OnlyParamSpec[()]().attr)  # revealed: (...) -> None
```

Specializing a union of generic classes supports the same `ParamSpec` argument forms.

```py
if input():
    class ConditionalParamSpec[**P]:
        attr: Callable[P, None]

else:
    class ConditionalParamSpec[**P]:
        attr: Callable[P, None]

def conditional_class_paramspec[**P](_: ConditionalParamSpec[P]) -> None: ...
def conditional_class_paramspec_list(_: ConditionalParamSpec[[int, str]]) -> None: ...
```

An explicit tuple expression (unlike an implicit one that omits the parentheses) is also accepted
when the `ParamSpec` is the only type variable. But, this isn't recommended is mainly a fallout of
it having the same AST as the one without the parentheses. Both mypy and Pyright also allow this.

```py
reveal_type(OnlyParamSpec[(int, str)]().attr)  # revealed: (int, str, /) -> None
```

<!-- fmt:off -->

```py
# error: [invalid-syntax]
reveal_type(OnlyParamSpec[]().attr)  # revealed: (...) -> None
```

<!-- fmt:on -->

The square brackets can be omitted when `ParamSpec` is the only type variable

```py
reveal_type(OnlyParamSpec[int, str]().attr)  # revealed: (int, str, /) -> None
reveal_type(OnlyParamSpec[int,]().attr)  # revealed: (int, /) -> None

# Even when there is only one element
reveal_type(OnlyParamSpec[Any]().attr)  # revealed: (Any, /) -> None
reveal_type(OnlyParamSpec[object]().attr)  # revealed: (object, /) -> None
reveal_type(OnlyParamSpec[int]().attr)  # revealed: (int, /) -> None
```

But, they cannot be omitted when there are multiple type variables.

```py
reveal_type(TypeVarAndParamSpec[int, []]().attr)  # revealed: () -> int
reveal_type(TypeVarAndParamSpec[int, [int, str]]().attr)  # revealed: (int, str, /) -> int
reveal_type(TypeVarAndParamSpec[int, [str]]().attr)  # revealed: (str, /) -> int
reveal_type(TypeVarAndParamSpec[int, ...]().attr)  # revealed: (...) -> int
reveal_type(ParamSpecAndTypeVar[[int, str], str]().attr)  # revealed: (int, str, /) -> str

# error: [invalid-type-arguments] "ParamSpec `P2` is unbound"
reveal_type(TypeVarAndParamSpec[int, P2]().attr)  # revealed: (...) -> int
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
reveal_type(TypeVarAndParamSpec[int, int]().attr)  # revealed: (...) -> int
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
reveal_type(TypeVarAndParamSpec[int, ()]().attr)  # revealed: (...) -> int
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be"
reveal_type(TypeVarAndParamSpec[int, (int, str)]().attr)  # revealed: (...) -> int
```

Nor can they be omitted when there are more than one `ParamSpec`.

```py
p = TwoParamSpec[[int, str], [int]]()
reveal_type(p.attr1)  # revealed: (int, str, /) -> None
reveal_type(p.attr2)  # revealed: (int, /) -> None

# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...`"
# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...`"
TwoParamSpec[int, str]
```

Specializing `ParamSpec` type variable using `typing.Any` isn't explicitly allowed by the spec but
both mypy and Pyright allow this and there are usages of this in the wild e.g.,
`staticmethod[Any, Any]`.

```py
reveal_type(TypeVarAndParamSpec[int, Any]().attr)  # revealed: (...) -> int
```

`...` has the same gradual behavior when used as a `ParamSpec` argument in a generic class,
regardless of the variance of the `ParamSpec`.

```py
from typing import Callable

class Command[**P]:
    callback: Callable[P, None]

# confirm that Command is invariant in P
def _(of_int: Command[int], of_bool: Command[bool]) -> None:
    a: Command[int] = of_bool  # error: [invalid-assignment]
    b: Command[bool] = of_int  # error: [invalid-assignment]

# but gradual signature is still assignable in both directions
def _(concrete: Command[[str]], gradual: Command[...]) -> None:
    a: Command[...] = concrete
    b: Command[[str]] = gradual
```

`ParamSpec` specializations in generic classes are compared using the callable parameter relation.
This avoids rejecting wrappers around callbacks that are safe to use with a positional-only callback
protocol.

```py
from collections.abc import Callable
from typing import Final

class Job[**P]:
    target: Final[Callable[P, None]]

    def __init__(self, target: Callable[P, None]) -> None:
        self.target = target

def named(x: int) -> None:
    pass

def defaulted(x: int | None = None) -> None:
    pass

def wrong(x: str) -> None:
    pass

named_job = Job(named)
defaulted_job = Job(defaulted)
wrong_job = Job(wrong)

def takes_int_job(job: Job[[int]]) -> None:
    pass

takes_int_job(named_job)
takes_int_job(defaulted_job)
takes_int_job(wrong_job)  # error: [invalid-argument-type]
```

## `ParamSpec` cannot specialize a `TypeVar`, and vice versa

<!-- snapshot-diagnostics -->

A `ParamSpec` is not a valid type argument for a regular `TypeVar`, and vice versa.

```py
from typing import Callable

class OnlyTypeVar[T]:
    attr: T

class TypeVarAndParamSpec[T, **P]:
    attr: Callable[P, T]

def f[**P, T]():
    # error: [invalid-type-arguments] "ParamSpec `P` cannot be used to specialize type variable `T`"
    a: OnlyTypeVar[P]

    # error: [invalid-type-arguments] "ParamSpec `P` cannot be used to specialize type variable `T`"
    b: TypeVarAndParamSpec[P, [int]]

class OnlyParamSpec[**P]:
    attr: Callable[P, None]

# This is fine due to the special case whereby `OnlyParamSpec[T]` is interpreted the same as
# `OnlyParamSpec[[T]]`, due to the fact that `OnlyParamSpec` is only generic over a single
# `ParamSpec` and no other type variables.
def func2[T](c: OnlyParamSpec[T], other: T):
    reveal_type(c.attr)  # revealed: (T@func2, /) -> None

class ParamSpecAndTypeVar[**P, T]:
    attr: Callable[P, T]

# error: [invalid-type-arguments] "Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...`"
def func3[T](c: ParamSpecAndTypeVar[T, int], other: T): ...
```

## Specialization when defaults are involved

```py
from typing import Callable, ParamSpec

class ParamSpecWithDefault1[**P1 = [int, str]]:
    attr: Callable[P1, None]

reveal_type(ParamSpecWithDefault1().attr)  # revealed: (int, str, /) -> None
reveal_type(ParamSpecWithDefault1[int]().attr)  # revealed: (int, /) -> None
```

```py
class ParamSpecWithDefault2[**P1 = ...]:
    attr: Callable[P1, None]

reveal_type(ParamSpecWithDefault2().attr)  # revealed: (...) -> None
reveal_type(ParamSpecWithDefault2[int, str]().attr)  # revealed: (int, str, /) -> None
```

```py
class ParamSpecWithDefault3[**P1, **P2 = P1]:
    attr1: Callable[P1, None]
    attr2: Callable[P2, None]

# `P1` hasn't been specialized, so it defaults to `...` gradual form
p1 = ParamSpecWithDefault3()
reveal_type(p1.attr1)  # revealed: (...) -> None
reveal_type(p1.attr2)  # revealed: (...) -> None

p2 = ParamSpecWithDefault3[[int, str]]()
reveal_type(p2.attr1)  # revealed: (int, str, /) -> None
reveal_type(p2.attr2)  # revealed: (int, str, /) -> None

p3 = ParamSpecWithDefault3[[int], [str]]()
reveal_type(p3.attr1)  # revealed: (int, /) -> None
reveal_type(p3.attr2)  # revealed: (str, /) -> None

class ParamSpecWithDefault4[**P1 = [int, str], **P2 = P1]:
    attr1: Callable[P1, None]
    attr2: Callable[P2, None]

p1 = ParamSpecWithDefault4()
reveal_type(p1.attr1)  # revealed: (int, str, /) -> None
reveal_type(p1.attr2)  # revealed: (int, str, /) -> None

p2 = ParamSpecWithDefault4[[int]]()
reveal_type(p2.attr1)  # revealed: (int, /) -> None
reveal_type(p2.attr2)  # revealed: (int, /) -> None

p3 = ParamSpecWithDefault4[[int], [str]]()
reveal_type(p3.attr1)  # revealed: (int, /) -> None
reveal_type(p3.attr2)  # revealed: (str, /) -> None

P2 = ParamSpec("P2")

# error: [invalid-generic-class] "Default of `P1` cannot reference out-of-scope type variable `P2`"
class ParamSpecWithDefault5[**P1 = P2]:
    attr: Callable[P1, None]
```

## Semantics

Most of these test cases are adopted from the
[typing documentation on `ParamSpec` semantics](https://typing.python.org/en/latest/spec/generics.html#semantics).

### Return type change using `ParamSpec` once

```py
from typing import Callable

def converter[**P](func: Callable[P, int]) -> Callable[P, bool]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> bool:
        func(*args, **kwargs)
        return True
    return wrapper

def f1(x: int, y: str) -> int:
    return 1

# This should preserve all the information about the parameters of `f1`
f2 = converter(f1)

reveal_type(f2)  # revealed: (x: int, y: str) -> bool

reveal_type(f1(1, "a"))  # revealed: int
reveal_type(f2(1, "a"))  # revealed: bool

# As it preserves the parameter kinds, the following should work as well
reveal_type(f2(1, y="a"))  # revealed: bool
reveal_type(f2(x=1, y="a"))  # revealed: bool
reveal_type(f2(y="a", x=1))  # revealed: bool

# error: [missing-argument] "No argument provided for required parameter `y`"
f2(1)
# error: [invalid-argument-type] "Argument is incorrect: Expected `int`, found `Literal["a"]`"
f2("a", "b")
```

The `converter` function act as a decorator here:

```py
@converter
def f3(x: int, y: str) -> int:
    return 1

reveal_type(f3)  # revealed: (x: int, y: str) -> bool

reveal_type(f3(1, "a"))  # revealed: bool
reveal_type(f3(x=1, y="a"))  # revealed: bool
reveal_type(f3(1, y="a"))  # revealed: bool
reveal_type(f3(y="a", x=1))  # revealed: bool

# error: [missing-argument] "No argument provided for required parameter `y`"
f3(1)
# error: [invalid-argument-type] "Argument is incorrect: Expected `int`, found `Literal["a"]`"
f3("a", "b")
```

### Return type change using the same `ParamSpec` multiple times

```py
from typing import Callable

def multiple[**P](func1: Callable[P, int], func2: Callable[P, int]) -> Callable[P, bool]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> bool:
        func1(*args, **kwargs)
        func2(*args, **kwargs)
        return True
    return wrapper
```

As per the spec,

> A user may include the same `ParamSpec` multiple times in the arguments of the same function, to
> indicate a dependency between multiple arguments. In these cases a type checker may choose to
> solve to a common behavioral supertype (i.e. a set of parameters for which all of the valid calls
> are valid in both of the subtypes), but is not obligated to do so.

TODO: Currently, we don't do this

```py
def xy(x: int, y: str) -> int:
    return 1

def yx(y: int, x: str) -> int:
    return 2

reveal_type(multiple(xy, xy))  # revealed: (x: int, y: str) -> bool

# The common supertype is `(int, str, /)` which is converting the positional-or-keyword parameters
# into positional-only parameters because the position of the types are the same.
# TODO: This shouldn't error
# error: [invalid-argument-type]
reveal_type(multiple(xy, yx))  # revealed: (x: int, y: str) -> bool

def keyword_only_with_default_1(*, x: int = 42) -> int:
    return 1

def keyword_only_with_default_2(*, y: int = 42) -> int:
    return 2

# The common supertype for two functions with only keyword-only parameters would be an empty
# parameter list i.e., `()`
# TODO: This shouldn't error
# error: [invalid-argument-type]
# revealed: (*, x: int = 42) -> bool
reveal_type(multiple(keyword_only_with_default_1, keyword_only_with_default_2))

def keyword_only1(*, x: int) -> int:
    return 1

def keyword_only2(*, y: int) -> int:
    return 2

# On the other hand, combining two functions with only keyword-only parameters does not have a
# common supertype, so it should result in an error.
# error: [invalid-argument-type] "Argument to function `multiple` is incorrect: Expected `(*, x: int) -> int`, found `def keyword_only2(*, y: int) -> int`"
reveal_type(multiple(keyword_only1, keyword_only2))  # revealed: (*, x: int) -> bool
```

### Constructors of user-defined generic class on `ParamSpec`

```py
from typing import Callable

class C[**P]:
    f: Callable[P, int]

    def __init__(self, f: Callable[P, int]) -> None:
        self.f = f

# Note that the return type must match exactly, since C is invariant on the return type of C.f.
def f(x: int, y: str) -> int:
    return True

c = C(f)
reveal_type(c.f)  # revealed: (x: int, y: str) -> int
```

### `ParamSpec` in prepended positional parameters

> If one of these prepended positional parameters contains a free `ParamSpec`, we consider that
> variable in scope for the purposes of extracting the components of that `ParamSpec`.

```py
from typing import Callable

def foo1[**P1](func: Callable[P1, int], *args: P1.args, **kwargs: P1.kwargs) -> int:
    return func(*args, **kwargs)

def foo1_with_extra_arg[**P1](func: Callable[P1, int], extra: str, *args: P1.args, **kwargs: P1.kwargs) -> int:
    return func(*args, **kwargs)

def foo2[**P2](func: Callable[P2, int], *args: P2.args, **kwargs: P2.kwargs) -> None:
    foo1(func, *args, **kwargs)

    # error: [invalid-argument-type] "Argument to function `foo1` is incorrect: Expected `P2@foo2.args`, found `Literal[1]`"
    foo1(func, 1, *args, **kwargs)

    # error: [invalid-argument-type] "Argument to function `foo1_with_extra_arg` is incorrect: Expected `str`, found `P2@foo2.args`"
    foo1_with_extra_arg(func, *args, **kwargs)

    foo1_with_extra_arg(func, "extra", *args, **kwargs)
```

Here, the first argument to `f` can specialize `P` to the parameters of the callable passed to it
which is then used to type the `ParamSpec` components used in `*args` and `**kwargs`.

```py
def f1(x: int, y: str) -> int:
    return 1

foo1(f1, 1, "a")
foo1(f1, x=1, y="a")
foo1(f1, 1, y="a")

# error: [missing-argument] "No arguments provided for required parameters `x`, `y` of function `foo1`"
foo1(f1)

# error: [missing-argument] "No argument provided for required parameter `y` of function `foo1`"
foo1(f1, 1)

# error: [invalid-argument-type] "Argument to function `foo1` is incorrect: Expected `str`, found `Literal[2]`"
foo1(f1, 1, 2)

# error: [too-many-positional-arguments] "Too many positional arguments to function `foo1`: expected 2, got 3"
foo1(f1, 1, "a", "b")

# error: [missing-argument] "No argument provided for required parameter `y` of function `foo1`"
# error: [unknown-argument] "Argument `z` does not match any known parameter of function `foo1`"
foo1(f1, x=1, z="a")
```

Inline literals passed through `ParamSpec` components should be inferred using the specialized
parameter types of the callable argument. This mirrors wrappers like `asyncio.to_thread` around
clients that expose large `Unpack[TypedDict]` keyword signatures, but also applies to other
context-sensitive literals.

```py
from typing import Callable, TypedDict, Unpack, overload

class Tag(TypedDict):
    Key: str
    Value: str

class PutObjectRequest(TypedDict):
    TagSet: list[Tag]

def put_tags(tags: list[Tag], /) -> None: ...
def put_object(**kwargs: Unpack[PutObjectRequest]) -> None: ...
def put_int_list(values: list[int], /) -> None: ...
def put_int_tuple(values: tuple[int, ...], /) -> None: ...
def put_int_tuples(values: list[tuple[int, ...]], /) -> None: ...
def to_thread_like[**P, R](func: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R:
    return func(*args, **kwargs)

def to_thread_like_keyword[**P, R](func: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
    return func(*args, **kwargs)

to_thread_like(put_tags, reveal_type([{"Key": "k", "Value": "v"}]))  # revealed: list[Tag]
to_thread_like(put_object, TagSet=reveal_type([{"Key": "k", "Value": "v"}]))  # revealed: list[Tag]
to_thread_like(put_int_list, reveal_type([1, 2]))  # revealed: list[int]
to_thread_like(put_int_tuple, reveal_type((1, 2)))  # revealed: tuple[Literal[1], Literal[2]]
to_thread_like(put_int_tuples, reveal_type([(1, 2)]))  # revealed: list[tuple[int, ...]]
to_thread_like_keyword(func=put_object, TagSet=reveal_type([{"Key": "k", "Value": "v"}]))  # revealed: list[Tag]
to_thread_like_keyword(TagSet=reveal_type([{"Key": "k", "Value": "v"}]), func=put_object)  # revealed: list[Tag]

class ThreadRunner:
    def run[**P, R](self, func: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R:
        return func(*args, **kwargs)

class ClassThreadRunner:
    @classmethod
    def run[**P, R](cls, func: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R:
        return func(*args, **kwargs)

runner = ThreadRunner()
runner.run(put_tags, reveal_type([{"Key": "k", "Value": "v"}]))  # revealed: list[Tag]
runner.run(put_object, TagSet=reveal_type([{"Key": "k", "Value": "v"}]))  # revealed: list[Tag]
runner.run(put_int_list, reveal_type([1, 2]))  # revealed: list[int]
runner.run(put_int_tuple, reveal_type((1, 2)))  # revealed: tuple[Literal[1], Literal[2]]
runner.run(put_int_tuples, reveal_type([(1, 2)]))  # revealed: list[tuple[int, ...]]
ClassThreadRunner.run(put_int_list, reveal_type([1, 2]))  # revealed: list[int]
ClassThreadRunner.run(put_int_tuple, reveal_type((1, 2)))  # revealed: tuple[Literal[1], Literal[2]]
ClassThreadRunner.run(put_int_tuples, reveal_type([(1, 2)]))  # revealed: list[tuple[int, ...]]

def requires_x(*, x: int) -> str:
    return ""

def with_flag[**P, R](func: Callable[P, R], flag: int, *args: P.args, **kwargs: P.kwargs) -> R:
    return func(*args, **kwargs)

with_flag(x=1, func=requires_x, flag=1)
with_flag(x=1, func=requires_x, flag="bad")  # error: [invalid-argument-type]

@overload
def overloaded_put_object(*, TagSet: list[Tag]) -> None: ...
@overload
def overloaded_put_object(*, func: object, TagSet: list[int]) -> None: ...
def overloaded_put_object(*, TagSet: object, func: object = None) -> None: ...

to_thread_like_keyword(TagSet=reveal_type([{"Key": "k", "Value": "v"}]), func=overloaded_put_object)  # revealed: list[Tag]
```

ParamSpec forwarding should not use raw unspecialized parameter types from a wrapped generic
callable as argument context. The forwarded list literals should be inferred the same way as in the
equivalent direct generic call, not with a raw `list[T]` context from the wrapped callable.

```py
from typing import Callable

def generic_pair[T](x: list[T], y: list[T], /) -> None: ...
def generic_pair_with_container[T](x: T, y: list[T], /) -> None: ...
def generic_identity_list[T](x: list[T], /) -> list[T]:
    raise NotImplementedError

def to_thread_like[**P, R](func: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R:
    return func(*args, **kwargs)

# TODO: This should not error once the call-expression type context specializes the generic
# wrapped callable before we infer forwarded `ParamSpec` arguments.
# error: [invalid-assignment]
union_list_result: list[int | str] = to_thread_like(generic_identity_list, reveal_type([1]))  # revealed: list[int]

# error: [invalid-argument-type]
# error: [invalid-argument-type]
to_thread_like(generic_pair, [1], reveal_type([""]))  # revealed: list[str]

# error: [invalid-argument-type]
to_thread_like(generic_pair_with_container, 1, reveal_type([""]))  # revealed: list[str]
```

### Specializing `ParamSpec` with another `ParamSpec`

```py
class Foo[**P]:
    def __init__(self, *args: P.args, **kwargs: P.kwargs) -> None:
        self.args = args
        self.kwargs = kwargs

def bar[**P](foo: Foo[P]) -> None:
    reveal_type(foo)  # revealed: Foo[P@bar]
    reveal_type(foo.args)  # revealed: P@bar.args
    reveal_type(foo.kwargs)  # revealed: P@bar.kwargs
```

ty will check whether the argument after `**` is a mapping type, but the inferred attribute type
preserves the parameter pack here, so it shouldn't error.

```py
from typing import Callable

def baz[**P](fn: Callable[P, None], foo: Foo[P]) -> None:
    fn(*foo.args, **foo.kwargs)
```

The `Unknown` can be eliminated by using annotating these attributes with `Final`:

```py
from typing import Final

class FooWithFinal[**P]:
    def __init__(self, *args: P.args, **kwargs: P.kwargs) -> None:
        self.args: Final = args
        self.kwargs: Final = kwargs

def with_final[**P](foo: FooWithFinal[P]) -> None:
    reveal_type(foo)  # revealed: FooWithFinal[P@with_final]
    reveal_type(foo.args)  # revealed: P@with_final.args
    reveal_type(foo.kwargs)  # revealed: P@with_final.kwargs
```

### Specializing `Self` when `ParamSpec` is involved

```py
class Foo[**P]:
    def method(self, *args: P.args, **kwargs: P.kwargs) -> str:
        return "hello"

foo = Foo[int, str]()

reveal_type(foo)  # revealed: Foo[(int, str, /)]
reveal_type(foo.method)  # revealed: bound method Foo[(int, str, /)].method(int, str, /) -> str
reveal_type(foo.method(1, "a"))  # revealed: str
```

### Gradual types propagate through `ParamSpec` inference

```py
from typing import Callable

def callable_identity[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    return func

@callable_identity
def f(env: dict[str, int]) -> None:
    pass

# revealed: (env: dict[str, int]) -> None
reveal_type(f)
```

### Transparent decorator passthrough

A decorator typed as `Callable[P, R] -> Callable[P, R]` preserves overload signatures.

```py
from typing import Callable, overload

def transparent[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    return func

@overload
def test(x: int) -> int: ...
@overload
def test(*, y: str) -> str: ...
@transparent
def test(x: int | None = None, *, y: str | None = None) -> int | str:
    raise NotImplementedError

reveal_type(test)  # revealed: Overload[(x: int) -> int, (*, y: str) -> str]
reveal_type(test(1))  # revealed: int
reveal_type(test(y="x"))  # revealed: str

# error: [no-matching-overload]
reveal_type(test(1, y="x"))  # revealed: Unknown

@transparent
def increment(value: int) -> int:
    return value + 1

reveal_type(increment)  # revealed: (value: int) -> int
reveal_type(increment(1))  # revealed: int
```

A type alias for `Callable[P, R]` can also be used to type a transparent decorator.

```py
type Fn[**P, R] = Callable[P, R]

def transparent[**P, R](func: Fn[P, R]) -> Fn[P, R]:
    return func

@overload
def alias_decorated(x: int) -> int: ...
@overload
def alias_decorated(*, y: str) -> str: ...
@transparent
def alias_decorated(x: int | None = None, *, y: str | None = None) -> int | str:
    raise NotImplementedError

reveal_type(alias_decorated)  # revealed: Overload[(x: int) -> int, (*, y: str) -> str]
reveal_type(alias_decorated(1))  # revealed: int
reveal_type(alias_decorated(y="x"))  # revealed: str
```

A transparent decorator returned by a decorator factory also preserves overload signatures.

```py
def transparent_factory[**P, R]() -> Callable[[Callable[P, R]], Callable[P, R]]:
    def decorator(func: Callable[P, R]) -> Callable[P, R]:
        return func

    return decorator

@overload
def decorated_factory(x: int) -> int: ...
@overload
def decorated_factory(*, y: str) -> str: ...
@transparent_factory()
def decorated_factory(x: int | None = None, *, y: str | None = None) -> int | str:
    raise NotImplementedError

reveal_type(decorated_factory)  # revealed: Overload[(x: int) -> int, (*, y: str) -> str]
reveal_type(decorated_factory(1))  # revealed: int
reveal_type(decorated_factory(y="x"))  # revealed: str
```

A callable protocol can also describe a transparent decorator returned by a factory. Stacked
decorators of this form preserve overload signatures independently.

```py
from typing import Callable, Literal, ParamSpec, Protocol, TypeVar, overload

P = ParamSpec("P")
R = TypeVar("R")

class IdentityFunction(Protocol):
    def __call__(self, func: Callable[P, R], /) -> Callable[P, R]: ...

def deprecate_streaming_parameter() -> IdentityFunction:
    raise NotImplementedError

def forward_old_opt_flags() -> IdentityFunction:
    raise NotImplementedError

class DataFrame: ...
class InProcessQuery: ...

class LazyFrame:
    @overload
    def collect(self, *, background: Literal[True]) -> InProcessQuery: ...
    @overload
    def collect(self, *, background: Literal[False] = False) -> DataFrame: ...
    @deprecate_streaming_parameter()
    @forward_old_opt_flags()
    def collect(self, *, background: bool = False) -> DataFrame | InProcessQuery:
        raise NotImplementedError

lazy_frame = LazyFrame()
reveal_type(lazy_frame.collect())  # revealed: DataFrame
reveal_type(lazy_frame.collect(background=False))  # revealed: DataFrame
reveal_type(lazy_frame.collect(background=True))  # revealed: InProcessQuery
```

### Overloads

#### Return type filtering

`overloaded.pyi`:

```pyi
from typing import overload

class A: ...
class B: ...
class C: ...

@overload
def f1(x: A) -> A: ...
@overload
def f1(x: B) -> B: ...
@overload
def f1(x: C) -> C: ...
@overload
def f2(x: A) -> A: ...
@overload
def f2(x: B) -> A: ...
```

```py
from typing import Callable
from overloaded import f1, f2, A, B, C

def change_return_type_a[**P](f: Callable[P, A]) -> Callable[P, C]:
    def nested(*args: P.args, **kwargs: P.kwargs) -> C:
        return C()
    return nested

def change_return_type_b[**P](f: Callable[P, B]) -> Callable[P, C]:
    def nested(*args: P.args, **kwargs: P.kwargs) -> C:
        return C()
    return nested

def change_union_return_type[**P](f: Callable[P, A | B]) -> Callable[P, C]:
    def nested(*args: P.args, **kwargs: P.kwargs) -> C:
        return C()
    return nested

reveal_type(change_return_type_a(f1))  # revealed: (x: A) -> C
reveal_type(change_union_return_type(f1))  # revealed: Overload[(x: A) -> C, (x: B) -> C]

reveal_type(change_return_type_a(f2))  # revealed: Overload[(x: A) -> C, (x: B) -> C]
reveal_type(change_union_return_type(f2))  # revealed: Overload[(x: A) -> C, (x: B) -> C]

# error: [invalid-argument-type]
reveal_type(change_return_type_b(f2))  # revealed: (...) -> C
```

#### Parameter type filtering

`overloaded.pyi`:

```pyi
from typing import overload

@overload
def int_int(x: int) -> int: ...
@overload
def int_int(x: str) -> int: ...
```

```py
from typing import Callable
from overloaded import int_int

def with_parameters[**P](f: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> Callable[P, str]:
    def nested(*args: P.args, **kwargs: P.kwargs) -> str:
        return str(f(*args, **kwargs))
    return nested

# TODO: This should reveal the matching overload instead
reveal_type(with_parameters(int_int, 1))  # revealed: Overload[(x: int) -> str, (x: str) -> str]
reveal_type(with_parameters(int_int, "a"))  # revealed: Overload[(x: int) -> str, (x: str) -> str]

# error: [invalid-argument-type] "Argument to function `with_parameters` is incorrect: Expected `int`, found `None`"
reveal_type(with_parameters(int_int, None))  # revealed: Overload[(x: int) -> str, (x: str) -> str]

def foo(int_or_str: int | str):
    # Argument type expansion leads to matching both overloads.
    # TODO: Should this be an error instead?
    reveal_type(with_parameters(int_int, int_or_str))  # revealed: Overload[(x: int) -> str, (x: str) -> str]

# Keyword argument matching should also work
# TODO: This should reveal the matching overload instead
reveal_type(with_parameters(int_int, x=1))  # revealed: Overload[(x: int) -> str, (x: str) -> str]
reveal_type(with_parameters(int_int, x="a"))  # revealed: Overload[(x: int) -> str, (x: str) -> str]

# No matching overload should error
# error: [invalid-argument-type]
reveal_type(with_parameters(int_int, 1.5))  # revealed: Overload[(x: int) -> str, (x: str) -> str]
```

### Overloads with multiple parameters

`overloaded.pyi`:

```pyi
from typing import overload

@overload
def multi(x: int, y: int) -> int: ...
@overload
def multi(x: str, y: str) -> str: ...
```

```py
from typing import Callable
from overloaded import multi

def run[**P, R](f: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
    return f(*args, **kwargs)

# Both arguments match first overload
# TODO: should reveal `int`
reveal_type(run(multi, 1, 2))  # revealed: int | str

# Both arguments match second overload
# TODO: should reveal `str`
reveal_type(run(multi, "a", "b"))  # revealed: int | str

# Mixed positional and keyword
# TODO: both should reveal `int`
reveal_type(run(multi, 1, y=2))  # revealed: int | str
reveal_type(run(multi, x=1, y=2))  # revealed: int | str

# No matching overload (int, str doesn't match either overload of `multi`)
# error: [invalid-argument-type]
reveal_type(run(multi, 1, "b"))  # revealed: int | str
```

### Overloads with substitution of `P.args` and `P.kwargs`

This is regression test for <https://github.com/astral-sh/ty/issues/2027>

```py
from typing import Callable, Never, overload

class Task[**P, R]:
    def __init__(self, func: Callable[P, R]) -> None:
        self.func = func

    @overload
    def __call__(self: "Task[P, R]", *args: P.args, **kwargs: P.kwargs) -> R: ...
    @overload
    def __call__(self: "Task[P, Never]", *args: P.args, **kwargs: P.kwargs) -> None: ...
    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R | None:
        return self.func(*args, **kwargs)

def returns_str(x: int) -> str:
    return str(x)

def never_returns(x: int) -> Never:
    raise Exception()

t1 = Task(returns_str)
reveal_type(t1)  # revealed: Task[(x: int), str]
reveal_type(t1(1))  # revealed: str
reveal_type(t1(x=1))  # revealed: str
# error: [no-matching-overload]
reveal_type(t1("a"))  # revealed: Unknown
# error: [no-matching-overload]
reveal_type(t1(y=1))  # revealed: Unknown

t2 = Task(never_returns)
reveal_type(t2)  # revealed: Task[(x: int), Never]
reveal_type(t2(1))  # revealed: Never
```

## ParamSpec attribute assignability

When comparing signatures with `ParamSpec` attributes (`P.args` and `P.kwargs`), two different
inferable `ParamSpec` attributes with the same kind are assignable to each other. This enables
method overrides where both methods have their own `ParamSpec`.

### Same attribute kind, both inferable

```py
from typing import Callable

class Parent:
    def method[**P](self, callback: Callable[P, None]) -> Callable[P, None]:
        return callback

class Child1(Parent):
    # This is a valid override: Q.args matches P.args, Q.kwargs matches P.kwargs
    def method[**Q](self, callback: Callable[Q, None]) -> Callable[Q, None]:
        return callback

# Both signatures use ParamSpec, so they should be compatible
def outer[**P](f: Callable[P, int]) -> Callable[P, int]:
    def inner[**Q](g: Callable[Q, int]) -> Callable[Q, int]:
        return g
    return inner(f)
```

We can explicitly mark it as an override using the `@override` decorator.

```py
from typing import override

class Child2(Parent):
    @override
    def method[**Q](self, callback: Callable[Q, None]) -> Callable[Q, None]:
        return callback
```

### One `ParamSpec` not inferable

Here, `P` is in a non-inferable position while `Q` is inferable. So, they are not considered
assignable.

```py
from typing import Callable

class Container[**P]:
    def method(self, f: Callable[P, None]) -> Callable[P, None]:
        return f

    def try_assign[**Q](self, f: Callable[Q, None]) -> Callable[Q, None]:
        # error: [invalid-return-type] "Return type does not match returned value: expected `(**Q@try_assign) -> None`, found `(**P@Container) -> None`"
        # error: [invalid-argument-type] "Argument to bound method `Container.method` is incorrect: Expected `(**P@Container) -> None`, found `(**Q@try_assign) -> None`"
        return self.method(f)
```

## `ParamSpec` inference with un-annotated return type

Regression test for an issue where `ParamSpec` inference failed when the callable we were inferring
from did not have an annotated return type.

```py
from typing import Callable

def infer_paramspec[**P](func: Callable[P, None]) -> Callable[P, None]:
    return func

def f(x: int, y: str):
    pass

reveal_type(infer_paramspec(f))  # revealed: (x: int, y: str) -> None
```

## Generic context preservation through `ParamSpec` decorators

When a generic function is decorated with a `ParamSpec`-based decorator, the generic context of the
decorated function should be preserved. This allows type inference to work correctly when calling
the decorated function.

Regression test for <https://github.com/astral-sh/ty/issues/2336>

### Basic

```py
from typing import Callable
from ty_extensions._internal import generic_context

def decorator[**P, T](func: Callable[P, T]) -> Callable[P, T]:
    return func

@decorator
def identity[T](value: T) -> T:
    return value

@decorator
def pair[T, U](first: T, second: U) -> tuple[T, U]:
    return (first, second)

# revealed: ty_extensions._internal.GenericContext[T@identity]
reveal_type(generic_context(identity))
# revealed: ty_extensions._internal.GenericContext[T@pair, U@pair]
reveal_type(generic_context(pair))

reveal_type(identity(1))  # revealed: Literal[1]
reveal_type(identity("hello"))  # revealed: Literal["hello"]

reveal_type(pair(1, "a"))  # revealed: tuple[Literal[1], Literal["a"]]
reveal_type(pair("x", 2.5))  # revealed: tuple[Literal["x"], float]
```

### Chained decorators with generic functions

```py
from typing import Callable

def decorator1[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    return func

def decorator2[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    return func

@decorator1
@decorator2
def chained_generic[T](value: T) -> T:
    return value

reveal_type(chained_generic(42))  # revealed: Literal[42]
reveal_type(chained_generic("test"))  # revealed: Literal["test"]
```

### Generic method decoration

```py
from typing import Callable
from ty_extensions._internal import generic_context

def method_decorator[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    return func

class Container:
    @method_decorator
    def generic_method[T](self, value: T) -> T:
        return value

c = Container()

# revealed: ty_extensions._internal.GenericContext[T@generic_method]
reveal_type(generic_context(c.generic_method))

reveal_type(c.generic_method)  # revealed: [T](value: T) -> T
reveal_type(c.generic_method(100))  # revealed: Literal[100]
reveal_type(c.generic_method([1, 2, 3]))  # revealed: list[int]
```

## Callable protocols with `ParamSpec` and class constructors

When a class is passed to a function expecting a callable protocol with `ParamSpec`, the `ParamSpec`
should be inferred from the class's constructor signature. This inferred signature must then be used
to validate any additional arguments that use the `ParamSpec` components.

```py
from typing import Protocol

class ParentClass: ...

class MyProto[**P](Protocol):
    def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ParentClass: ...

class MyType(ParentClass):
    def __init__(self, x: int):
        pass

def create[**P](p: MyProto[P], *args: P.args, **kwargs: P.kwargs) -> ParentClass:
    return p(*args, **kwargs)

# When MyType is passed, P should be inferred as [x: int] from MyType's __init__.
# Since create() requires *args: P.args and **kwargs: P.kwargs, and P is [x: int],
# we must provide the `x` argument.

# error: [missing-argument] "No argument provided for required parameter `x`"
create(MyType)

# These should work since we're providing the required argument
create(MyType, 1)
create(MyType, x=1)

# error: [invalid-argument-type]
create(MyType, "wrong type")
```

A class with no required constructor parameters should work without additional arguments:

```py
class NoArgs(ParentClass):
    pass

create(NoArgs)  # OK - P is inferred as []
```

Multiple parameters:

```py
class MultiParam(ParentClass):
    def __init__(self, x: int, y: str):
        pass

# error: [missing-argument]
create(MultiParam)

# error: [missing-argument]
create(MultiParam, 1)

create(MultiParam, 1, "hello")
create(MultiParam, x=1, y="hello")
create(MultiParam, 1, y="hello")

# error: [too-many-positional-arguments]
create(MultiParam, 1, "hello", "extra")

# error: [invalid-argument-type]
create(MultiParam, "wrong", "hello")
```

Optional parameters (default values):

```py
class WithDefaults(ParentClass):
    def __init__(self, x: int, y: str = "default"):
        pass

# error: [missing-argument]
create(WithDefaults)

# OK - y has a default
create(WithDefaults, 1)
create(WithDefaults, 1, "custom")
create(WithDefaults, x=1)
create(WithDefaults, x=1, y="custom")
```

Keyword-only parameters:

```py
class KeywordOnly(ParentClass):
    def __init__(self, *, x: int):
        pass

# error: [missing-argument]
create(KeywordOnly)

# Passing positional where keyword-only is expected
# error: [missing-argument]
# error: [too-many-positional-arguments]
create(KeywordOnly, 1)

create(KeywordOnly, x=1)
```

Positional-only parameters:

```py
class PositionalOnly(ParentClass):
    def __init__(self, x: int, /):
        pass

# error: [missing-argument]
create(PositionalOnly)

create(PositionalOnly, 1)

# error: [positional-only-parameter-as-kwarg]
create(PositionalOnly, x=1)
```

The protocol requires the return type to be `ParentClass`, so passing a class that doesn't inherit
from `ParentClass` should produce an error:

```py
class Unrelated:
    def __init__(self, x: int):
        pass

# error: [invalid-argument-type]
create(Unrelated, 1)
```

When the protocol has parameters before the `ParamSpec` (i.e., `Concatenate`-style signatures), the
callable should still match correctly and `P` should be inferred as empty:

```py
def my_factory(arg: str) -> int:
    return 0

class Factory[**P](Protocol):
    def __call__(self, arg: str, *args: P.args, **kwargs: P.kwargs) -> int: ...

def call_factory[**P](ctr: Factory[P], *args: P.args, **kwargs: P.kwargs) -> int:
    return ctr("", *args, **kwargs)

call_factory(my_factory)
```
