w6c+selfhost+lib: Hare-style variadic call sites
Param-decl `name: T...` (Tparam.variadic=1, type []T), call-site
gather of N args into a fresh `[N]T`, forward via `xs...`, full
selfhost mirror, and lib/fmt graduated to the Hare shape.
Frontend:
- parse: `T...` after a param's type stamps Node.op=TK_ELLIPSIS
and breaks out (variadic must be last).
- check: resolve_type N_TFN / build_fn_type wrap the param type
as []T and set tp->variadic. N_CALL accepts either a tail of
args assignable to T (gather) or a single `xs...` spread of
[]T (forward); both bypass the "too many args" check on the
variadic slot.
- type: type_eq compares Tparam.variadic.
Cgen (cstage):
- call site: when the callee has a variadic last param,
materialise the tail args into a frame-resident `[N]T` via
localoff, write a 24B slice descriptor (ptr,len,cap), and
splice a synthesised N_IDENT into args[] so the downstream
widen/eval/pop loops see one slice slot. Tagged-element types
route each store through cg_widen_tagged_store. Forwarding
skips gather: the N_SPREAD wrapper is replaced with its inner
slice expression. Empty form writes {nil,0,0}. args[] / widen[]
bump from 16 to 64 to accommodate Hare's mixed-arg printers.
Selfhost mirror:
- lib/ww/parse: `T...` mark on N_PARAM.op.
- cgen: varargseq counter on Cg; scanlocals reserves
@vararg_d_N + @vararg_sl_N per variadic call (seq recorded on
N_CALL.uval so cgcall picks the same names). cgcall does the
same gather/forward and N_IDENT splice. cgfnparams treats
variadic params as 24B slice slots via a synthesised TSLICE
tnode. pushargsrev skips the tagged-widen detection for
variadic params (effective type is []T, not tagged).
- rhstargetname now recognises N_TRUE/N_FALSE/N_RUNELIT and
typed N_INTLIT so the variant-tag lookup finds bool/rune/iN
variants instead of falling through to "first non-str" (which
misassigned tag 0 to bool in tagged unions like formattable).
lib/fmt graduated: print/println/fprint/fprintln/errorln/fatal
take `args: formattable...`. Bare `error` (no -ln) is skipped —
the leaf name collides with strconv's `type error = !(invalid |
overflow)` under the driver's flat namespace.
Tests: 5 new e2e rows (plain gather, zero-arg, tagged element,
forwarding, fmt.println end-to-end). lib/CLAUDE.md workaround
paragraph replaced with the Hare-shape description.
This commit is contained in:
@@ -1558,6 +1558,75 @@ static const struct row rows[] = {
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};", 42 }, /* 1 + 2 + 39 = 42 */
|
||||
/* Hare-style variadic gather: `args: T...` declares an N-arg
|
||||
* variadic; the call-site materialises N values into a fresh
|
||||
* `[N]T` and synthesises a {ptr,len,cap} slice for the param.
|
||||
* Plain element type (i64): no widening, MOVQ-per-element. */
|
||||
{ "fn sum(args: i64...) i64 = {\n"
|
||||
" let s: i64 = 0i64;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < args.len) { s += args[i]; i += 1; };\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" return sum(1i64, 2i64, 3i64, 7i64, 9i64, 20i64): i32;\n"
|
||||
"};", 42 },
|
||||
/* Variadic with zero args: empty-slice descriptor {nil,0,0}.
|
||||
* Confirms the gather path doesn't crash on N=0. */
|
||||
{ "fn sum(args: i64...) i64 = {\n"
|
||||
" let s: i64 = 0i64;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < args.len) { s += args[i]; i += 1; };\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let a: i64 = sum();\n"
|
||||
" let b: i64 = sum(42i64);\n"
|
||||
" return (a + b): i32;\n"
|
||||
"};", 42 },
|
||||
/* Variadic with tagged-union element type: each gathered arg
|
||||
* widens to the variant's slot shape (tag@+0, payload@+8). The
|
||||
* runtime match-dispatch reads (i64=1)+(str.len=2)+(bool=39)=42. */
|
||||
{ "type formattable = (i64 | str | bool);\n"
|
||||
"fn sumtag(args: formattable...) i64 = {\n"
|
||||
" let s: i64 = 0i64;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < args.len) {\n"
|
||||
" match (args[i]) {\n"
|
||||
" case let n: i64 => s += n;\n"
|
||||
" case let v: str => s += v.len: i64;\n"
|
||||
" case let b: bool => { if (b) { s += 39i64; }; };\n"
|
||||
" };\n"
|
||||
" i += 1;\n"
|
||||
" };\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = { return sumtag(1i64, \"hi\", true): i32; };", 42 },
|
||||
/* Variadic forwarding: `wrap(args...)` passes the local slice
|
||||
* directly to `sum`, no re-gather. Mirrors Hare's wrapper shape
|
||||
* (`fn println(args: formattable...) = fprintln(os.stdout, args...)`). */
|
||||
{ "fn sum(args: i64...) i64 = {\n"
|
||||
" let s: i64 = 0i64;\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < args.len) { s += args[i]; i += 1; };\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"fn wrap(prefix: i64, args: i64...) i64 = {\n"
|
||||
" return prefix + sum(args...);\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" return wrap(2i64, 1i64, 2i64, 3i64, 4i64, 5i64, 7i64, 18i64): i32;\n"
|
||||
"};", 42 },
|
||||
/* lib/fmt user-side: `fmt.println(args: formattable...)` gathers
|
||||
* mixed-type args at the call site. End-to-end exercises the
|
||||
* lib/fmt graduation: the wrapper-chain `println → fprintln →
|
||||
* fprint` is itself variadic-forwarding, so this validates both
|
||||
* gather (at main) and `args...` forward (inside lib/fmt). The
|
||||
* exit code is bytes printed (`hello 7\n` = 8). */
|
||||
{ "use fmt;\n"
|
||||
"fn main() i32 = {\n"
|
||||
" return fmt.println(\"hello\", 7i64): i32;\n"
|
||||
"};", 8 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user