From 45910ff9661dd11aad5f6da0293b45828bdf0a61 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 22 May 2026 03:09:22 +0900 Subject: [PATCH] selfhost/cmd/wcc: variadic param N_TSLICE wrap at installparams (cascade Commit 3, #25/#41) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit installparams normalises `T...` p.lhs to []T so N_IDENT lookups (s.decl.lhs) and cgen's variadic-slot synthesis see the effective slice — mirrors cstage check.c:455 `tp->type = type_slice(c->a, pt)` and harec check_func_type. cgendecl/cgenexpr drop the on-the-fly slicewrap and consume p.lhs directly; cgenexpr cgcall peels the N_TSLICE wrap when reading the element predicates / esz (Ken's gate: cstage cgen.c:4352 `vsu->kind == TY_SLICE`), and passes `velem` — not the wrap — into cgwidentaggedstore (cstage cgen.c :4382). Closes the 22 N_DOT + 9 N_INDEX fires from #36 with the cascade absorbed by e662156 (#39 arm-6 recursion). Class B "case: not a variant of scrutinee" did NOT fire post-#39, so Commit 2 (#40 tagged_select_subtype) was not needed. --- selfhost/cmd/w6c/main.combined.ww | 62 +++++++++++++++++++++------- selfhost/cmd/wcc/cgendecl.ww | 10 +++-- selfhost/cmd/wcc/cgenexpr.ww | 38 ++++++++++++----- selfhost/cmd/wcc/check.ww | 14 +++++++ selfhost/cmd/wwdump/main.combined.ww | 62 +++++++++++++++++++++------- 5 files changed, 141 insertions(+), 45 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index e871915f..c82f079a 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -9690,6 +9690,20 @@ fn installparams(c: *checker, params: *node) void = { let p: *node = params; for (p != nil) { if (p.kind == nkind.N_PARAM) { + // Hare-style variadic `T...`: normalize p.lhs to []T so + // downstream consumers (N_IDENT exprtype lookups via + // s.decl.lhs, cgen's variadic-slot synthesis) see the + // effective slice type. Mirrors cstage check.c:455 + // `tp->type = type_slice(c->a, pt)` and harec + // check_func_type. Surface-fidelity preserved: wwdump + // -a runs parser only and never reaches this mutation. + if (p.op == tkind.TK_ELLIPSIS) { + if (p.lhs != nil && p.lhs.kind != nkind.N_TSLICE) { + let sl: *node = newnode(nkind.N_TSLICE, "", 0, 0); + sl.lhs = p.lhs; + p.lhs = sl; + }; + }; let nm: str = p.str; if (nm.len > 0) { checkmoduleshadow(c, nm, "param"); @@ -17127,29 +17141,42 @@ fn cgcall(c: *cgen, n: *node) void = { // scalars to 8, mismatching the stride at the // callee read site — runtime miscompile in // `(rune...)` callees per #36. + // check.ww installparams promotes varp.lhs to + // []T (mirrors cstage check.c:455 tp->type + // wrap). Element predicates / esz read varp.lhs + // .lhs; Ken's gate: only deref when the wrap + // shape is confirmed N_TSLICE (mirrors cstage + // cgen.c:4352 `vsu->kind == TY_SLICE` guard). + let velem: *node = varp.lhs; + if (varp.lhs != nil + && varp.lhs.kind == nkind.N_TSLICE) { + velem = varp.lhs.lhs; + }; let esz: i32 = 8; - if (varp.lhs != nil) { - if (varp.lhs.kind == nkind.N_TNAME) { - let ps: i32 = primsize(varp.lhs.str); + if (velem != nil) { + if (velem.kind == nkind.N_TNAME) { + let ps: i32 = primsize(velem.str); if (ps > 0) { esz = ps; } - else { esz = slotsize(c, varp.lhs); }; + else { esz = slotsize(c, velem); }; } else { - esz = slotsize(c, varp.lhs); + esz = slotsize(c, velem); }; }; 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 velemtagged: bool = istaggedtype(c, velem); + let velemstr: bool = isstrtype(c, velem); + let velemslice: bool = isslicetype(c, velem); let doff: i32 = 0; if (nvar > 0) { doff = localadd(c, dname, nvar * esz, nil); }; // #60: vararg gather builds a {ptr,len,cap} slice // descriptor — route through tyslicesize so #34's - // slice-header bump propagates here. + // slice-header bump propagates here. varp.lhs is + // already the []T wrap from installparams, so we + // consume it directly (re-slicewrap → [][]T). let soff: i32 = localadd(c, sname, tyslicesize(): i32, - slicewrap(c, varp.lhs)); + varp.lhs); let aa2: *node = n.list; let kk3: i32 = 0; for (kk3 < nfixed_v) { @@ -17169,7 +17196,10 @@ fn cgcall(c: *cgen, n: *node) void = { for (aa2 != nil) { let slot: i32 = doff + j * esz; if (velemtagged) { - cgwidentaggedstore(c, varp.lhs, + // dst is the per-element tagged type; + // pass velem (cstage cgen.c:4382 passes + // velem, not the slice wrap vsu). + cgwidentaggedstore(c, velem, aa2, "BP", slot, esz); } else { if (velemstr) { cgexpr(c, aa2); @@ -21541,11 +21571,13 @@ fn cgfnparams(c: *cgen, params: *node) void = { 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. + // slice (3 register words / 24B). p.lhs is already + // the []T wrap installed by check.ww installparams + // (mirrors cstage check.c:455 tp->type promotion), so + // we consume it directly — re-wrapping via slicewrap + // would yield [][]T. if (p.op == tkind.TK_ELLIPSIS) { - let tn: *node = slicewrap(c, p.lhs); + let tn: *node = p.lhs; if (idx + 3 <= 6) { let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); emitline("\tMOVQ\t"); diff --git a/selfhost/cmd/wcc/cgendecl.ww b/selfhost/cmd/wcc/cgendecl.ww index 7a2737dc..745d79a3 100644 --- a/selfhost/cmd/wcc/cgendecl.ww +++ b/selfhost/cmd/wcc/cgendecl.ww @@ -39,11 +39,13 @@ fn cgfnparams(c: *cgen, params: *node) void = { 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. + // slice (3 register words / 24B). p.lhs is already + // the []T wrap installed by check.ww installparams + // (mirrors cstage check.c:455 tp->type promotion), so + // we consume it directly — re-wrapping via slicewrap + // would yield [][]T. if (p.op == tkind.TK_ELLIPSIS) { - let tn: *node = slicewrap(c, p.lhs); + let tn: *node = p.lhs; if (idx + 3 <= 6) { let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); emitline("\tMOVQ\t"); diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index 723a1044..21b83cdd 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -3078,29 +3078,42 @@ fn cgcall(c: *cgen, n: *node) void = { // scalars to 8, mismatching the stride at the // callee read site — runtime miscompile in // `(rune...)` callees per #36. + // check.ww installparams promotes varp.lhs to + // []T (mirrors cstage check.c:455 tp->type + // wrap). Element predicates / esz read varp.lhs + // .lhs; Ken's gate: only deref when the wrap + // shape is confirmed N_TSLICE (mirrors cstage + // cgen.c:4352 `vsu->kind == TY_SLICE` guard). + let velem: *node = varp.lhs; + if (varp.lhs != nil + && varp.lhs.kind == nkind.N_TSLICE) { + velem = varp.lhs.lhs; + }; let esz: i32 = 8; - if (varp.lhs != nil) { - if (varp.lhs.kind == nkind.N_TNAME) { - let ps: i32 = primsize(varp.lhs.str); + if (velem != nil) { + if (velem.kind == nkind.N_TNAME) { + let ps: i32 = primsize(velem.str); if (ps > 0) { esz = ps; } - else { esz = slotsize(c, varp.lhs); }; + else { esz = slotsize(c, velem); }; } else { - esz = slotsize(c, varp.lhs); + esz = slotsize(c, velem); }; }; 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 velemtagged: bool = istaggedtype(c, velem); + let velemstr: bool = isstrtype(c, velem); + let velemslice: bool = isslicetype(c, velem); let doff: i32 = 0; if (nvar > 0) { doff = localadd(c, dname, nvar * esz, nil); }; // #60: vararg gather builds a {ptr,len,cap} slice // descriptor — route through tyslicesize so #34's - // slice-header bump propagates here. + // slice-header bump propagates here. varp.lhs is + // already the []T wrap from installparams, so we + // consume it directly (re-slicewrap → [][]T). let soff: i32 = localadd(c, sname, tyslicesize(): i32, - slicewrap(c, varp.lhs)); + varp.lhs); let aa2: *node = n.list; let kk3: i32 = 0; for (kk3 < nfixed_v) { @@ -3120,7 +3133,10 @@ fn cgcall(c: *cgen, n: *node) void = { for (aa2 != nil) { let slot: i32 = doff + j * esz; if (velemtagged) { - cgwidentaggedstore(c, varp.lhs, + // dst is the per-element tagged type; + // pass velem (cstage cgen.c:4382 passes + // velem, not the slice wrap vsu). + cgwidentaggedstore(c, velem, aa2, "BP", slot, esz); } else { if (velemstr) { cgexpr(c, aa2); diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index c0d367df..5dd6085f 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -2726,6 +2726,20 @@ fn installparams(c: *checker, params: *node) void = { let p: *node = params; for (p != nil) { if (p.kind == nkind.N_PARAM) { + // Hare-style variadic `T...`: normalize p.lhs to []T so + // downstream consumers (N_IDENT exprtype lookups via + // s.decl.lhs, cgen's variadic-slot synthesis) see the + // effective slice type. Mirrors cstage check.c:455 + // `tp->type = type_slice(c->a, pt)` and harec + // check_func_type. Surface-fidelity preserved: wwdump + // -a runs parser only and never reaches this mutation. + if (p.op == tkind.TK_ELLIPSIS) { + if (p.lhs != nil && p.lhs.kind != nkind.N_TSLICE) { + let sl: *node = newnode(nkind.N_TSLICE, "", 0, 0); + sl.lhs = p.lhs; + p.lhs = sl; + }; + }; let nm: str = p.str; if (nm.len > 0) { checkmoduleshadow(c, nm, "param"); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 74c625c4..62e5c2b1 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -9690,6 +9690,20 @@ fn installparams(c: *checker, params: *node) void = { let p: *node = params; for (p != nil) { if (p.kind == nkind.N_PARAM) { + // Hare-style variadic `T...`: normalize p.lhs to []T so + // downstream consumers (N_IDENT exprtype lookups via + // s.decl.lhs, cgen's variadic-slot synthesis) see the + // effective slice type. Mirrors cstage check.c:455 + // `tp->type = type_slice(c->a, pt)` and harec + // check_func_type. Surface-fidelity preserved: wwdump + // -a runs parser only and never reaches this mutation. + if (p.op == tkind.TK_ELLIPSIS) { + if (p.lhs != nil && p.lhs.kind != nkind.N_TSLICE) { + let sl: *node = newnode(nkind.N_TSLICE, "", 0, 0); + sl.lhs = p.lhs; + p.lhs = sl; + }; + }; let nm: str = p.str; if (nm.len > 0) { checkmoduleshadow(c, nm, "param"); @@ -17127,29 +17141,42 @@ fn cgcall(c: *cgen, n: *node) void = { // scalars to 8, mismatching the stride at the // callee read site — runtime miscompile in // `(rune...)` callees per #36. + // check.ww installparams promotes varp.lhs to + // []T (mirrors cstage check.c:455 tp->type + // wrap). Element predicates / esz read varp.lhs + // .lhs; Ken's gate: only deref when the wrap + // shape is confirmed N_TSLICE (mirrors cstage + // cgen.c:4352 `vsu->kind == TY_SLICE` guard). + let velem: *node = varp.lhs; + if (varp.lhs != nil + && varp.lhs.kind == nkind.N_TSLICE) { + velem = varp.lhs.lhs; + }; let esz: i32 = 8; - if (varp.lhs != nil) { - if (varp.lhs.kind == nkind.N_TNAME) { - let ps: i32 = primsize(varp.lhs.str); + if (velem != nil) { + if (velem.kind == nkind.N_TNAME) { + let ps: i32 = primsize(velem.str); if (ps > 0) { esz = ps; } - else { esz = slotsize(c, varp.lhs); }; + else { esz = slotsize(c, velem); }; } else { - esz = slotsize(c, varp.lhs); + esz = slotsize(c, velem); }; }; 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 velemtagged: bool = istaggedtype(c, velem); + let velemstr: bool = isstrtype(c, velem); + let velemslice: bool = isslicetype(c, velem); let doff: i32 = 0; if (nvar > 0) { doff = localadd(c, dname, nvar * esz, nil); }; // #60: vararg gather builds a {ptr,len,cap} slice // descriptor — route through tyslicesize so #34's - // slice-header bump propagates here. + // slice-header bump propagates here. varp.lhs is + // already the []T wrap from installparams, so we + // consume it directly (re-slicewrap → [][]T). let soff: i32 = localadd(c, sname, tyslicesize(): i32, - slicewrap(c, varp.lhs)); + varp.lhs); let aa2: *node = n.list; let kk3: i32 = 0; for (kk3 < nfixed_v) { @@ -17169,7 +17196,10 @@ fn cgcall(c: *cgen, n: *node) void = { for (aa2 != nil) { let slot: i32 = doff + j * esz; if (velemtagged) { - cgwidentaggedstore(c, varp.lhs, + // dst is the per-element tagged type; + // pass velem (cstage cgen.c:4382 passes + // velem, not the slice wrap vsu). + cgwidentaggedstore(c, velem, aa2, "BP", slot, esz); } else { if (velemstr) { cgexpr(c, aa2); @@ -21541,11 +21571,13 @@ fn cgfnparams(c: *cgen, params: *node) void = { 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. + // slice (3 register words / 24B). p.lhs is already + // the []T wrap installed by check.ww installparams + // (mirrors cstage check.c:455 tp->type promotion), so + // we consume it directly — re-wrapping via slicewrap + // would yield [][]T. if (p.op == tkind.TK_ELLIPSIS) { - let tn: *node = slicewrap(c, p.lhs); + let tn: *node = p.lhs; if (idx + 3 <= 6) { let off: i32 = localadd(c, nm, tyslicesize(): i32, tn); emitline("\tMOVQ\t");