Skip to content

Variadic keyword generics - #51

Merged
KotlinIsland merged 3 commits into
mainfrom
variadic-keyword-generics
Jul 24, 2026
Merged

Variadic keyword generics#51
KotlinIsland merged 3 commits into
mainfrom
variadic-keyword-generics

Conversation

@KotlinIsland

Copy link
Copy Markdown
Owner

No description provided.

unpack keyword packs with `**`, matching the declaration

`**kwargs: **Kwargs` is now the spelling — the star count follows the pack's
declaration (`[**Kwargs]`), the way `*args: *Ts` follows `[*Ts]`. the single
star was the TypeVarTuple spelling and no longer parses on a pack, so there is
one way to write it

`{**Kwargs}` (splicing a pack into a dict literal type) gets a real diagnostic
instead of two misleading ones. it is not supported: a synthesized TypedDict is
a `ClassType::NonGeneric` by construction, so a specialization never reaches its
schema — the same gap leaves `T` unsubstituted in `{"a": T}`

infer keyword packs from constructor keyword arguments

`**kwargs: *Kwargs` unpacks a keyword-variadic pack into a parameter list, so
`A(a=1, b="s")` now solves `Kwargs` to `(a=int, b=str)`. the single `*` unpacks
the variadic, mirroring `*args: *Ts`

the pack is solved as a whole rather than per-argument — the per-argument path
would bind it to one field's type. an already-specialized pack expands into
keyword-only parameters through the existing starred-annotation hook, so the
constructor call is checked against the fields

lowers to the runtime `ParamSpec` spelling `**kwargs: Kwargs.kwargs`

drive-by, both pre-existing and both needed by the above:
- `init(*args)` / `init(**kwargs)` read the `*` as a parameter modifier and
  failed with "`*` is not a valid modifier on an `init` parameter"
- the pep695 polyfill's typevar rename skipped vararg/kwarg annotations, so a
  polyfilled `**kwargs: Kwargs.kwargs` kept the pre-rename name

snapshot the bare keyword-pack diagnostic

it carries subdiagnostics, so pin them rather than only the primary message

added keyword-variadic packs (`class A[**Kwargs]`)

in a `.by` file the pep-695 `**Name` spelling now declares a keyword-variadic
pack — an ordered name→type mapping — instead of a `ParamSpec`. specialized by
keyword (`A[foo=int, bar=str]`), unpacked with `**` in a callable arrow, where
its fields become keyword-only parameters

reuses the paramspec engine: a pack's value is the same callable-shaped value,
so relations, variance, substitution and concatenate all come along. the split
is `is_parameter_pack` (callable-shaped value) vs `is_paramspec` (truthful)

confined to `.by`; `.byi` keeps `ParamSpec` for typeshed interop
`class A[**P]` in a .py file now reverses to `class A[P: (*: *, **: *)]` — an
anonymous variadic and an anonymous keyword-variadic, both admitting anything.
every parameter list is a subtype of it, so the bound ranges over all parameter
lists, which is exactly a ParamSpec. `[**P]` could not stay as-is: in a .by file
it declares a keyword-variadic pack

the structural form is what docs/basedpython/features/generics.md already
documented; the implementation had been recognising a bare `Parameters` marker
name instead. that name is now gone, along with the import-stripping it needed —
one spelling, no magic name, and the pair round-trips

`*` is now accepted as the type of an anonymous parameter field, so `(*: *)`
parses; it reuses the top-star marker a subscript `[*]` already produces
`A[(int, foo: str)]` built every field as a positional-only parameter, so
`foo: str` came back as a bare `str` and the name could not be passed. it now
reveals `A[(int, /, foo: str)]` and `foo` binds by keyword

the callable arrow already knew every field form, so both now share one builder,
`infer_parameter_spec_elements`. that also fixes the arrow's `*` marker: the
keyword-only branch sat behind an `after_star` test that the preceding arm had
already claimed, so it was dead and `(int, *, foo: str)` produced a positional
`foo`

the `/` shown after `int` is real — a bare field has no name to be passed by
@KotlinIsland
KotlinIsland merged commit b8fcf74 into main Jul 24, 2026
56 of 61 checks passed
@KotlinIsland
KotlinIsland deleted the variadic-keyword-generics branch July 24, 2026 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant