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:
@@ -20,6 +20,81 @@ use typ;
|
||||
use sym;
|
||||
use strconv;
|
||||
|
||||
// ---- variadic-call helpers (Hare-style `T...` param) -----------------
|
||||
|
||||
// slicewrap — synthesise an N_TSLICE node wrapping the given element
|
||||
// type AST. Used by the Hare-style variadic path so the local entry
|
||||
// for the param (callee side) and the call-site slice descriptor
|
||||
// (caller side) both advertise their effective type as []ELEM —
|
||||
// every isslicetype / nodeisslice check then succeeds naturally.
|
||||
fn slicewrap(c: *cgen, elem: *node) *node = {
|
||||
let s: *node = newnode(c.a, nkind.N_TSLICE, "", 0, 0);
|
||||
s.lhs = elem;
|
||||
return s;
|
||||
};
|
||||
|
||||
// findvariadicparam — walk a param-list head and return the variadic
|
||||
// param node (the one with op == TK_ELLIPSIS) plus the count of
|
||||
// non-variadic params before it. Returns nil/0 when no variadic.
|
||||
// nfixed_out cannot be nil.
|
||||
fn findvariadicparam(ps: *node, nfixed_out: *i32) *node = {
|
||||
*nfixed_out = 0;
|
||||
let p: *node = ps;
|
||||
for (p != nil) {
|
||||
if (p.kind == nkind.N_PARAM) {
|
||||
if (p.op == tkind.TK_ELLIPSIS) {
|
||||
return p;
|
||||
};
|
||||
*nfixed_out += 1;
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// callee_variadic_param — convenience wrapper: looks up the callee
|
||||
// by name and finds its variadic param + nfixed. Returns nil if the
|
||||
// callee isn't registered or has no variadic param.
|
||||
fn callee_variadic_param(c: *cgen, callee: *node, nfixed_out: *i32) *node = {
|
||||
*nfixed_out = 0;
|
||||
if (callee == nil) { return nil; };
|
||||
let cnm: str;
|
||||
cnm.ptr = nil; cnm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { cnm = callee.str; };
|
||||
if (callee.kind == nkind.N_DOT) { cnm = callee.str; };
|
||||
if (cnm.len == 0) { return nil; };
|
||||
let ps: *node = fnparamslookup(c, cnm);
|
||||
return findvariadicparam(ps, nfixed_out);
|
||||
};
|
||||
|
||||
// mkvarargname — fresh local-slot name "<prefix><seq>". Used for
|
||||
// the per-variadic-call scratch buffers (`@vararg_d_N` for the
|
||||
// element-data buffer, `@vararg_sl_N` for the 24B slice descriptor)
|
||||
// where N is recorded on the N_CALL node at scanlocals time so both
|
||||
// the prologue reservation and the call-site emission agree.
|
||||
fn mkvarargname(c: *cgen, prefix: str, seq: i32) str = {
|
||||
let buf: [128]u8;
|
||||
let i: i32 = 0;
|
||||
let j: i32 = 0;
|
||||
for (j < prefix.len) {
|
||||
buf[i] = prefix[j];
|
||||
i += 1; j += 1;
|
||||
};
|
||||
let ns: str = strconv.i64tos(seq: i64, strconv.base.DEC);
|
||||
let n: i32 = ns.len;
|
||||
let dk: i32 = 0;
|
||||
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
|
||||
let total: i32 = i + n;
|
||||
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
|
||||
let k: i32 = 0;
|
||||
for (k < total) { p[k] = buf[k]; k += 1; };
|
||||
p[total] = 0u8;
|
||||
let r: str;
|
||||
r.ptr = p;
|
||||
r.len = total;
|
||||
return r;
|
||||
};
|
||||
|
||||
// ---- expression cgen -------------------------------------------------
|
||||
|
||||
// pushargsrev — recursively walks the arg list, evaluates rightmost
|
||||
@@ -45,21 +120,31 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
let widentag: i32 = 0;
|
||||
if (param != nil) {
|
||||
if (param.kind == nkind.N_PARAM) {
|
||||
let ptype: *node = param.lhs;
|
||||
if (istaggedtype(c, ptype)) {
|
||||
let aistagged: bool = false;
|
||||
if (arg.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, arg.str);
|
||||
if (lc != nil) {
|
||||
aistagged = istaggedtype(c, lc.tnode);
|
||||
// Hare-style variadic `T...`: effective param type is
|
||||
// []T (slice). The arg here is the synthesised slice
|
||||
// descriptor (or a forwarded `xs...` slice), not a
|
||||
// value of T being widened into a tagged slot — skip
|
||||
// the widening detection so the slice-ident fast path
|
||||
// at the bottom of pushargsrev gets the push.
|
||||
if (param.op == tkind.TK_ELLIPSIS) {
|
||||
widensz = 0;
|
||||
} else {
|
||||
let ptype: *node = param.lhs;
|
||||
if (istaggedtype(c, ptype)) {
|
||||
let aistagged: bool = false;
|
||||
if (arg.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, arg.str);
|
||||
if (lc != nil) {
|
||||
aistagged = istaggedtype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
if (!aistagged) {
|
||||
widensz = slotsize(c, ptype);
|
||||
let tagged: *node = resolvetagged(c, ptype);
|
||||
let t: i32 = taggedvariantindex(c, tagged, arg);
|
||||
if (t < 0) { t = 0; };
|
||||
widentag = t;
|
||||
};
|
||||
};
|
||||
if (!aistagged) {
|
||||
widensz = slotsize(c, ptype);
|
||||
let tagged: *node = resolvetagged(c, ptype);
|
||||
let t: i32 = taggedvariantindex(c, tagged, arg);
|
||||
if (t < 0) { t = 0; };
|
||||
widentag = t;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1487,6 +1572,16 @@ fn rhstargetname(c: *cgen, rhs: *node) str = {
|
||||
return nm;
|
||||
};
|
||||
if (rhs.kind == nkind.N_STRLIT) { return "str"; };
|
||||
if (rhs.kind == nkind.N_TRUE) { return "bool"; };
|
||||
if (rhs.kind == nkind.N_FALSE) { return "bool"; };
|
||||
if (rhs.kind == nkind.N_RUNELIT) { return "rune"; };
|
||||
if (rhs.kind == nkind.N_INTLIT) {
|
||||
// Typed int literal (`42i64`, `3u8`): suffix names the
|
||||
// concrete variant so flatvariantidx finds it. Untyped
|
||||
// literals (tsuffix=="") fall through to the isstr scan.
|
||||
let s: str = rhs.tsuffix;
|
||||
if (s.len > 0) { return s; };
|
||||
};
|
||||
// `T{}` carries its type name on the lhs N_IDENT — the parser
|
||||
// builds `N_STRUCTLIT{ lhs = N_IDENT("T"), list = fields }`.
|
||||
// Needed so `return eof{};` (variant of a tagged union) resolves
|
||||
|
||||
Reference in New Issue
Block a user