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:
@@ -290,6 +290,50 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Hare-style variadic call: reserve @vararg_d_<seq> for the
|
||||
// element data and @vararg_sl_<seq> for the 24B slice
|
||||
// descriptor. The seq is recorded on the N_CALL node so
|
||||
// cgcall picks the same names regardless of walk order
|
||||
// (scanlocals descends LTR; pushargsrev evaluates RTL).
|
||||
let nfixed: i32 = 0;
|
||||
let varp: *node = callee_variadic_param(c, n.lhs, &nfixed);
|
||||
if (varp != nil) {
|
||||
let nargs: i32 = 0;
|
||||
let aw: *node = n.list;
|
||||
for (aw != nil) { nargs += 1; aw = aw.next; };
|
||||
let nvar: i32 = nargs - nfixed;
|
||||
if (nvar < 0) { nvar = 0; };
|
||||
let forwarding: bool = false;
|
||||
if (nvar == 1) {
|
||||
let aa: *node = n.list;
|
||||
let k0: i32 = 0;
|
||||
for (k0 < nfixed) { aa = aa.next; k0 += 1; };
|
||||
if (aa != nil) {
|
||||
if (aa.kind == nkind.N_SPREAD) {
|
||||
forwarding = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!forwarding) {
|
||||
let seq: i32 = c.varargseq;
|
||||
n.uval = seq: u64;
|
||||
c.varargseq += 1;
|
||||
let esz: i32 = slotsize(c, varp.lhs);
|
||||
if (esz < 1) { esz = 1; };
|
||||
let dname: str = mkvarargname(c, "@vararg_d_", seq);
|
||||
let sname: str = mkvarargname(c, "@vararg_sl_", seq);
|
||||
if (nvar > 0) {
|
||||
if (!scanseenmark(c, dname)) {
|
||||
let dsz: i32 = nvar * esz;
|
||||
if ((dsz & 7) != 0) {
|
||||
dsz = (dsz + 7) & ~7;
|
||||
};
|
||||
total += dsz;
|
||||
};
|
||||
};
|
||||
if (!scanseenmark(c, sname)) { total += 24; };
|
||||
};
|
||||
};
|
||||
};
|
||||
if (n.lhs != nil) { total += scanlocals(c, n.lhs); };
|
||||
if (n.rhs != nil) { total += scanlocals(c, n.rhs); };
|
||||
@@ -321,6 +365,39 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
for (p != nil) {
|
||||
if (p.kind == nkind.N_PARAM) {
|
||||
let nm: str = p.str;
|
||||
// Hare-style variadic `T...`: callee receives a []T
|
||||
// slice (3 register words / 24B). Mirror the slice-
|
||||
// param spill below but use a synthesised TSLICE
|
||||
// tnode so body references see the slot as a slice.
|
||||
if (p.op == tkind.TK_ELLIPSIS) {
|
||||
let tn: *node = slicewrap(c, p.lhs);
|
||||
if (idx + 3 <= 6) {
|
||||
let off: i32 = localadd(c, nm, 24, tn);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, tn, 16 + stkcursor*8);
|
||||
stkcursor += 3;
|
||||
};
|
||||
p = p.next;
|
||||
continue;
|
||||
};
|
||||
if (isfloattype(c, p.lhs)) {
|
||||
// Float param: SysV uses the XMM stream
|
||||
// (X0..X7). 8B (f64) or 4B (f32) slot.
|
||||
@@ -494,15 +571,20 @@ fn cgfn(c: *cgen, fn_: *node) void = {
|
||||
let frame: i32 = 0;
|
||||
for (scanp != nil) {
|
||||
if (scanp.kind == nkind.N_PARAM) {
|
||||
if (istaggedtype(c, scanp.lhs)) { frame += slotsize(c, scanp.lhs); }
|
||||
// Hare-style variadic `T...`: param is []T inside
|
||||
// the callee, so it occupies a 24B slice slot.
|
||||
if (scanp.op == tkind.TK_ELLIPSIS) { frame += 24; }
|
||||
else { if (istaggedtype(c, scanp.lhs)) { frame += slotsize(c, scanp.lhs); }
|
||||
else { if (isslicetype(c, scanp.lhs)) { frame += 24; }
|
||||
else { if (isstrtype(c, scanp.lhs)) { frame += 16; }
|
||||
else { frame += 8; }; }; };
|
||||
else { frame += 8; }; }; }; };
|
||||
scanseenmark(c, scanp.str);
|
||||
};
|
||||
scanp = scanp.next;
|
||||
};
|
||||
c.varargseq = 0;
|
||||
if (fn_.body != nil) { frame += scanlocals(c, fn_.body); };
|
||||
c.varargseq = 0;
|
||||
// Drop the stubs so emission rebuilds c.locals with real offsets.
|
||||
c.locals = nil;
|
||||
if ((frame & 15) != 0) {
|
||||
|
||||
Reference in New Issue
Block a user