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:
@@ -2142,6 +2142,147 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
calleeparams = fnparamslookup(c, callee.str);
|
||||
};
|
||||
};
|
||||
// Hare-style variadic last param: gather N tail args into a
|
||||
// frame-resident [N]T (`@vararg_d_<seq>`) plus a 24B slice
|
||||
// descriptor (`@vararg_sl_<seq>`), then splice a synthesised
|
||||
// N_IDENT pointing at the descriptor into n.list so the rest
|
||||
// of the call machinery sees one slice slot for the variadic.
|
||||
// Forwarding shape (`xs...`) skips the gather: the spread's
|
||||
// inner slice expression replaces the wrapper in place. Empty
|
||||
// (no trailing args) writes a {nil, 0, 0} descriptor. The seq
|
||||
// matches the one scanlocals stamped on n.uval.
|
||||
{
|
||||
let nfixed_v: i32 = 0;
|
||||
let varp: *node = callee_variadic_param(c, callee, &nfixed_v);
|
||||
if (varp != nil) {
|
||||
let nargs0: i32 = 0;
|
||||
let aw: *node = n.list;
|
||||
for (aw != nil) { nargs0 += 1; aw = aw.next; };
|
||||
let nvar: i32 = nargs0 - nfixed_v;
|
||||
if (nvar < 0) { nvar = 0; };
|
||||
let forwarding: bool = false;
|
||||
if (nvar == 1) {
|
||||
let aaf: *node = n.list;
|
||||
let kk: i32 = 0;
|
||||
for (kk < nfixed_v) {
|
||||
aaf = aaf.next;
|
||||
kk += 1;
|
||||
};
|
||||
if (aaf != nil) {
|
||||
if (aaf.kind == nkind.N_SPREAD) {
|
||||
forwarding = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (forwarding) {
|
||||
let prev: *node = nil;
|
||||
let cur2: *node = n.list;
|
||||
let kk2: i32 = 0;
|
||||
for (kk2 < nfixed_v) {
|
||||
prev = cur2;
|
||||
cur2 = cur2.next;
|
||||
kk2 += 1;
|
||||
};
|
||||
let inner: *node = cur2.lhs;
|
||||
if (inner != nil) { inner.next = nil; };
|
||||
if (prev == nil) { n.list = inner; }
|
||||
else { prev.next = inner; };
|
||||
} else {
|
||||
let seq: i32 = n.uval: i32;
|
||||
let dname: str = mkvarargname(c, "@vararg_d_", seq);
|
||||
let sname: str = mkvarargname(c, "@vararg_sl_", seq);
|
||||
let esz: i32 = slotsize(c, varp.lhs);
|
||||
if (esz < 1) { esz = 1; };
|
||||
let velemtagged: bool = istaggedtype(c, varp.lhs);
|
||||
let velemstr: bool = isstrtype(c, varp.lhs);
|
||||
let velemslice: bool = isslicetype(c, varp.lhs);
|
||||
let doff: i32 = 0;
|
||||
if (nvar > 0) {
|
||||
doff = localadd(c, dname, nvar * esz, nil);
|
||||
};
|
||||
let soff: i32 = localadd(c, sname, 24,
|
||||
slicewrap(c, varp.lhs));
|
||||
let aa2: *node = n.list;
|
||||
let kk3: i32 = 0;
|
||||
for (kk3 < nfixed_v) {
|
||||
aa2 = aa2.next;
|
||||
kk3 += 1;
|
||||
};
|
||||
let j: i32 = 0;
|
||||
let prevarg: *node = n.list;
|
||||
if (nfixed_v == 0) { prevarg = nil; }
|
||||
else {
|
||||
let kk4: i32 = 0;
|
||||
for (kk4 < nfixed_v - 1) {
|
||||
prevarg = prevarg.next;
|
||||
kk4 += 1;
|
||||
};
|
||||
};
|
||||
for (aa2 != nil) {
|
||||
let slot: i32 = doff + j * esz;
|
||||
if (velemtagged) {
|
||||
cgwidentaggedstore(c, varp.lhs,
|
||||
aa2, slot, esz);
|
||||
} else { if (velemstr) {
|
||||
cgexpr(c, aa2);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(slot: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
} else { if (velemslice) {
|
||||
cgexpr(c, aa2);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(slot: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((slot + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
cgexpr(c, aa2);
|
||||
let op: str = "MOVQ";
|
||||
if (esz == 1) { op = "MOVB"; }
|
||||
else { if (esz == 4) { op = "MOVL"; }; };
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
emitoff(slot: i64);
|
||||
emitline("(BP)\n");
|
||||
}; }; };
|
||||
j += 1;
|
||||
aa2 = aa2.next;
|
||||
};
|
||||
if (nvar > 0) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(doff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
} else {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(soff: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(nvar: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((soff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((soff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
let sn: *node = newnode(c.a, nkind.N_IDENT,
|
||||
"", 0, 0);
|
||||
sn.str = sname;
|
||||
if (prevarg == nil) { n.list = sn; }
|
||||
else { prevarg.next = sn; };
|
||||
};
|
||||
};
|
||||
};
|
||||
let nargs: i32 = pushargsrev(c, n.list, calleeparams);
|
||||
// Pop forward. Float args were pushed as 8 bytes from X0 via
|
||||
// SUBQ+MOVSD; pop into the XMM stream (X0..X7). Everything else
|
||||
|
||||
Reference in New Issue
Block a user