lang: slice-of-protocol variadic ..xs: []P erases each arg to the protocol
packVariadicCallArgs stored the raw concrete arg into a [N x P] array when the
element type was a protocol, so an 8-byte struct landed in a 16-byte {ctx,
vtable} slot -> garbage vtable -> Bus error on dispatch. Now, when the slice
element type is a protocol, each arg is xx-erased to the protocol value via
buildProtocolErasure (same impl-driven machinery as the xx cast). This makes
..xs: []P the runtime, protocol-erased counterpart to the comptime
heterogeneous pack ..xs: P (which stays comptime-only): xs[runtime_i].method()
now works in an ordinary loop.
specs.md: full variadic/pack form-comparison table (concrete-vs-erased,
comptime-vs-runtime). Regression: examples/202. Issue 0052 (FIXED). 237 green.
This commit is contained in:
38
specs.md
38
specs.md
@@ -963,6 +963,11 @@ path_join :: (..parts: []string) -> string { ... }
|
||||
trailing args into a stack-allocated `[N x T]` and passes a slice over it.
|
||||
- For `[]Any`, each trailing arg is boxed into `Any` (type tag + payload) before
|
||||
packing; `args[i]` reads back the boxed value.
|
||||
- For `[]Protocol` (the element type is a protocol, e.g. `..xs: []Show`), each
|
||||
trailing arg is `xx`-erased to a protocol value `{ctx, vtable}` (impl-driven,
|
||||
like `xx`) and packed into a runtime `[N]Protocol`. `xs[runtime_i].method()`
|
||||
then dispatches through the protocol — this is the **runtime** counterpart to
|
||||
the comptime heterogeneous pack `..xs: Protocol`.
|
||||
- A `..` spread at the call site unpacks an existing slice/array into the variadic
|
||||
tail: `sum(..arr)`.
|
||||
- The heterogeneous comptime-pack form `..$args: []Type` binds per-position
|
||||
@@ -972,18 +977,31 @@ path_join :: (..parts: []string) -> string { ... }
|
||||
|
||||
A **pack** is a comptime sequence of per-position-typed arguments. Unlike a
|
||||
slice variadic (`..xs: []T`, one uniform element type, a runtime slice), a pack
|
||||
binds a *distinct* type to each position and exists only at compile time. Three
|
||||
declaration forms exist:
|
||||
binds a *distinct* type to each position and exists only at compile time.
|
||||
|
||||
| Form | Element typing | Body view |
|
||||
|---|---|---|
|
||||
| `..xs: []T` | uniform `T` | runtime `[]T` slice |
|
||||
| `..$xs: []Type` | per-position comptime *types* | comptime type list |
|
||||
| `..xs: Protocol` | per-position — each arg conforms to `Protocol` with its own type-arg | per-position-typed pack |
|
||||
The full family of variadic/pack forms and how they differ:
|
||||
|
||||
The third form is the heterogeneous pack. `map :: (mapper: ..., ..sources:
|
||||
ValueListenable) -> ...` accepts any number of trailing args, each some
|
||||
`ValueListenable(T)` for a possibly-different `T`.
|
||||
| Form | Element types | Lives at | `xs[i]` index | `xs[i]` yields | `xs.len` |
|
||||
|---|---|---|---|---|---|
|
||||
| `..xs: []T` | one uniform `T` | **runtime** (slice) | runtime or comptime | `T` | runtime |
|
||||
| `..xs: []Any` | mixed, **boxed** to `Any` | **runtime** (slice) | runtime or comptime | `Any` (match/unwrap to use) | runtime |
|
||||
| `..xs: []P` *(P a protocol)* | mixed, **erased** to `P` `{ctx,vtable}` | **runtime** (slice) | runtime or comptime | `P` (call protocol methods) | runtime |
|
||||
| `..xs: P` *(pack)* | per-position **concrete**, each conforms to `P` | **comptime** (no runtime value) | comptime only (literal / `inline for` cursor) | the concrete element, **viewed through `P`** | comptime int |
|
||||
| `..$args` / `..$xs: []Type` | per-position comptime **types** | **comptime** | comptime only | element value/type (reflection) | comptime int |
|
||||
|
||||
Key axis — **concrete vs erased, comptime vs runtime**:
|
||||
- `..xs: P` (pack) keeps each element's *concrete* type but is **comptime-only**:
|
||||
`xs[i]` needs a compile-time index (a literal or an `inline for` cursor); a
|
||||
runtime index is an error (a pack has no runtime representation). Use it when
|
||||
you need per-position types (monomorphization, `xs.T` / `xs.value` projection).
|
||||
- `..xs: []P` (slice of protocol) **erases** each element to the protocol value
|
||||
but is **runtime**: `xs[runtime_i].method()` works in an ordinary loop. Use it
|
||||
when you need to iterate the args at runtime and only the protocol interface
|
||||
matters. It is the runtime counterpart to the pack.
|
||||
|
||||
The heterogeneous pack (`..xs: P`) is what powers `map :: (mapper: ...,
|
||||
..sources: ValueListenable) -> ...`: it accepts any number of trailing args,
|
||||
each some `ValueListenable(T)` for a possibly-different `T`.
|
||||
|
||||
A pack is **not a runtime value** — it lowers to N typed positional parameters
|
||||
(zero overhead). The body refers to elements only through the comptime forms
|
||||
|
||||
Reference in New Issue
Block a user