wcc_ww/cgen: #77 alias-NAMED global ARRAY emit — tichase at the dispatch entry (g-fold G2)

ww half of the #77+#78 fused g-fold train; completes the family. cs
half landed as the previous commit (G1) — the two ship together, one
gated train, per the fuse ruling on both tasks.

Root: the global DATA emit walk dispatched on the UNCHASED decl tnode —
a NO-PEEL consumer (zero `.under` tokens on the path; it never learned
aliases exist). An alias-typed global array's N_TNAME matched no arm
and the documented skip-policy ate the decl: w6c_ww referenced
main.g(SB) but emitted zero DATAW → loud `w6l: undefined reference to
main.g` on every direct alias-global array row (ken NEW-1, all k_gidx*
shapes). Every other kind was already chased (letvarisstr/isslice/
isfloat/isstruct walk aliaslookup chains; the tuple gate walks tnodes;
emitarraydata/emitslicedata chase tinfo internally; letemitsize walks —
registration was never the gap), probe-confirmed: only array rows
failed ww-side.

Fix: ONE tichase at the dispatch entry, per the spec's entry-point rule
— not per-arm. Dispatch arms touched (enumerated):
  emitletdataw (cgen.ww): hoisted `dti = tichase(d.lhs.type_)` at the
    per-decl entry; the isarr8 scalar-shortcut gate and the array arm
    now key on dti.kind == TY_ARRAY (were d.lhs.kind == N_TARRAY) and
    emitarraydata receives dti; the struct zero-fill arm's inline
    TY_NAMED loop collapses into the same dti (ef93b16 precedent,
    byte-neutral). str/float/struct/slice/tuple gates unchanged.
  letpreintern (cgen.ww): the #18 [N]str array-leg gate keyed on the
    N_TARRAY tnode while its body already chased the tinfo — gate now
    keys on the chased kind, so alias-typed [N]str globals pre-intern
    their _S_ labels in decl order (label-order parity with cstage;
    the inner elem chase collapses into tichase).
For non-alias decls tichase is identity (same tinfo pointer) — the
emitted bytes are unchanged by construction; full byte-id invariant
holds (test-unit 288/288 incl. the new table).

main.combined.ww (w6c + wwdump) regenerated by `make` — diff verified
content-identical to the cgen.ww hunks, nothing else.

Graduation table committed as test/wcc/944_alias_global_decl_run.c —
23 rows x {cs run, ww run, byte-id} = 69 checks green. This table IS
the permanent guard: the path is lint-invisible (NO-PEEL — nothing for
the future peellint to see), so only a runtime+byte-id row pins it.
Rows: plain control; alias array 1-lvl read/write/decl-order; 2-lvl
read + order-permuted write (the #78 silent saved-BP rows); [4]u32
narrow-esz; scalar/str/f64/f32 2-lvl; alias-of-named-struct field w/r
(the cs SEGV-at-one-user-level row) + 3-layer + STRUCTLIT init; slice
2-lvl literal; [2]str 1-lvl/2-lvl (letpreintern label leg); def-side
2-lvl array/struct/float; no-regression holds (alias global SLICE,
alias ELEMENT [2]row). Values >255, LAST element asserted.

Probe-OUT rows documented in the test header, filed not pinned: #86
(named-tuple global init: cs checker loud-reject vs ww accept), #87
(plain tagged global: cs silent-wrong vs ww loud-reject, non-alias).
This commit is contained in:
2026-06-05 20:53:47 +09:00
parent 00f71b9781
commit 486f7f87f9
5 changed files with 526 additions and 81 deletions

View File

@@ -38257,23 +38257,15 @@ export fn letpreintern(c: *cgen, file: *node) void = {
// so emitstrarraydata's DATAR rows find an _S_ rodata
// row. Must match that helper's interning order exactly
// to keep labels stable.
// g-fold #77: gate on the CHASED tinfo kind — the
// N_TARRAY tnode test missed alias-typed [N]str
// globals, desyncing label order vs cstage.
let handled: bool = false;
if (d.lhs != nil && r != nil) {
if (d.lhs.kind == nkind.N_TARRAY
let au: *tinfo = tichase(d.lhs.type_: *tinfo);
if (au != nil && au.kind == tykind.TY_ARRAY
&& r.kind == nkind.N_ARRLIT) {
let au: *tinfo = d.lhs.type_: *tinfo;
for (au != nil && au.kind == tykind.TY_NAMED) {
au = au.under;
};
let eu: *tinfo = nil;
if (au != nil) {
if (au.kind == tykind.TY_ARRAY) {
eu = au.sub;
for (eu != nil && eu.kind == tykind.TY_NAMED) {
eu = eu.under;
};
};
};
let eu: *tinfo = tichase(au.sub);
if (eu != nil && eu.kind == tykind.TY_STR) {
handled = true;
let alen: i32 = au.alen: i32;
@@ -39302,6 +39294,15 @@ fn emitletdataw(c: *cgen, file: *node) void = {
let sz: i32 = letemitsize(c, d);
let issg: bool = letvarisstruct(c, nm);
let fsz: i32 = letvarisfloat(c, nm);
// g-fold #77: ONE chase at the dispatch entry. The
// array gates below keyed on the N_TARRAY tnode —
// an alias-typed global's N_TNAME matched no arm
// and the skip-policy ate the decl: no DATAW,
// undefined reference at link. The str/float/
// struct/slice/tuple gates already alias-walk
// (letvaris* / the tlt tnode walk) and stay put.
let dti: *tinfo = nil;
if (d.lhs != nil) { dti = tichase(d.lhs.type_: *tinfo); };
// C-t3 (#48): tuple global — slot-laid DATAW
// row (+ DATAR ptr patches for str elements)
// via emittupledata. Unsupported element
@@ -39363,8 +39364,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// below handles it and the duplicate DATAW would
// otherwise differ across stages on user code.
let isarr8: bool = false;
if (d.lhs != nil) {
if (d.lhs.kind == nkind.N_TARRAY) { isarr8 = true; };
if (dti != nil) {
if (dti.kind == tykind.TY_ARRAY) { isarr8 = true; };
};
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup) {
let v: u64 = 0u64;
@@ -39516,13 +39517,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
if (issg) {
if (d.rhs == nil) {
let zsz: i32 = sz;
if (d.lhs != nil) {
let ti: *tinfo = d.lhs.type_: *tinfo;
for (ti != nil && ti.kind == tykind.TY_NAMED) {
ti = ti.under;
};
if (ti != nil) { zsz = ti.size: i32; };
};
if (dti != nil) { zsz = dti.size: i32; };
emitline("DATAW ");
emitsymnamehint(c, nm, d.nmod);
emitline("(SB),\"");
@@ -39544,8 +39539,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// fold-failures don't corrupt the DATA literal.
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
// the same helper with rhs=nil → zero-fill branch.
if (d.lhs != nil) {
if (d.lhs.kind == nkind.N_TARRAY) {
if (dti != nil) {
if (dti.kind == tykind.TY_ARRAY) {
let rh: *node = d.rhs;
let route: bool = false;
if (rh == nil) { route = true; };
@@ -39555,9 +39550,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
};
};
if (route) {
let at: *tinfo = d.lhs.type_: *tinfo;
emitarraydata(c, "DATAW", nm,
d.nmod, at, rh);
d.nmod, dti, rh);
};
};
};

View File

@@ -1296,23 +1296,15 @@ export fn letpreintern(c: *cgen, file: *node) void = {
// so emitstrarraydata's DATAR rows find an _S_ rodata
// row. Must match that helper's interning order exactly
// to keep labels stable.
// g-fold #77: gate on the CHASED tinfo kind — the
// N_TARRAY tnode test missed alias-typed [N]str
// globals, desyncing label order vs cstage.
let handled: bool = false;
if (d.lhs != nil && r != nil) {
if (d.lhs.kind == nkind.N_TARRAY
let au: *tinfo = tichase(d.lhs.type_: *tinfo);
if (au != nil && au.kind == tykind.TY_ARRAY
&& r.kind == nkind.N_ARRLIT) {
let au: *tinfo = d.lhs.type_: *tinfo;
for (au != nil && au.kind == tykind.TY_NAMED) {
au = au.under;
};
let eu: *tinfo = nil;
if (au != nil) {
if (au.kind == tykind.TY_ARRAY) {
eu = au.sub;
for (eu != nil && eu.kind == tykind.TY_NAMED) {
eu = eu.under;
};
};
};
let eu: *tinfo = tichase(au.sub);
if (eu != nil && eu.kind == tykind.TY_STR) {
handled = true;
let alen: i32 = au.alen: i32;
@@ -2341,6 +2333,15 @@ fn emitletdataw(c: *cgen, file: *node) void = {
let sz: i32 = letemitsize(c, d);
let issg: bool = letvarisstruct(c, nm);
let fsz: i32 = letvarisfloat(c, nm);
// g-fold #77: ONE chase at the dispatch entry. The
// array gates below keyed on the N_TARRAY tnode —
// an alias-typed global's N_TNAME matched no arm
// and the skip-policy ate the decl: no DATAW,
// undefined reference at link. The str/float/
// struct/slice/tuple gates already alias-walk
// (letvaris* / the tlt tnode walk) and stay put.
let dti: *tinfo = nil;
if (d.lhs != nil) { dti = tichase(d.lhs.type_: *tinfo); };
// C-t3 (#48): tuple global — slot-laid DATAW
// row (+ DATAR ptr patches for str elements)
// via emittupledata. Unsupported element
@@ -2402,8 +2403,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// below handles it and the duplicate DATAW would
// otherwise differ across stages on user code.
let isarr8: bool = false;
if (d.lhs != nil) {
if (d.lhs.kind == nkind.N_TARRAY) { isarr8 = true; };
if (dti != nil) {
if (dti.kind == tykind.TY_ARRAY) { isarr8 = true; };
};
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup) {
let v: u64 = 0u64;
@@ -2555,13 +2556,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
if (issg) {
if (d.rhs == nil) {
let zsz: i32 = sz;
if (d.lhs != nil) {
let ti: *tinfo = d.lhs.type_: *tinfo;
for (ti != nil && ti.kind == tykind.TY_NAMED) {
ti = ti.under;
};
if (ti != nil) { zsz = ti.size: i32; };
};
if (dti != nil) { zsz = dti.size: i32; };
emitline("DATAW ");
emitsymnamehint(c, nm, d.nmod);
emitline("(SB),\"");
@@ -2583,8 +2578,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// fold-failures don't corrupt the DATA literal.
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
// the same helper with rhs=nil → zero-fill branch.
if (d.lhs != nil) {
if (d.lhs.kind == nkind.N_TARRAY) {
if (dti != nil) {
if (dti.kind == tykind.TY_ARRAY) {
let rh: *node = d.rhs;
let route: bool = false;
if (rh == nil) { route = true; };
@@ -2594,9 +2589,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
};
};
if (route) {
let at: *tinfo = d.lhs.type_: *tinfo;
emitarraydata(c, "DATAW", nm,
d.nmod, at, rh);
d.nmod, dti, rh);
};
};
};

View File

@@ -38257,23 +38257,15 @@ export fn letpreintern(c: *cgen, file: *node) void = {
// so emitstrarraydata's DATAR rows find an _S_ rodata
// row. Must match that helper's interning order exactly
// to keep labels stable.
// g-fold #77: gate on the CHASED tinfo kind — the
// N_TARRAY tnode test missed alias-typed [N]str
// globals, desyncing label order vs cstage.
let handled: bool = false;
if (d.lhs != nil && r != nil) {
if (d.lhs.kind == nkind.N_TARRAY
let au: *tinfo = tichase(d.lhs.type_: *tinfo);
if (au != nil && au.kind == tykind.TY_ARRAY
&& r.kind == nkind.N_ARRLIT) {
let au: *tinfo = d.lhs.type_: *tinfo;
for (au != nil && au.kind == tykind.TY_NAMED) {
au = au.under;
};
let eu: *tinfo = nil;
if (au != nil) {
if (au.kind == tykind.TY_ARRAY) {
eu = au.sub;
for (eu != nil && eu.kind == tykind.TY_NAMED) {
eu = eu.under;
};
};
};
let eu: *tinfo = tichase(au.sub);
if (eu != nil && eu.kind == tykind.TY_STR) {
handled = true;
let alen: i32 = au.alen: i32;
@@ -39302,6 +39294,15 @@ fn emitletdataw(c: *cgen, file: *node) void = {
let sz: i32 = letemitsize(c, d);
let issg: bool = letvarisstruct(c, nm);
let fsz: i32 = letvarisfloat(c, nm);
// g-fold #77: ONE chase at the dispatch entry. The
// array gates below keyed on the N_TARRAY tnode —
// an alias-typed global's N_TNAME matched no arm
// and the skip-policy ate the decl: no DATAW,
// undefined reference at link. The str/float/
// struct/slice/tuple gates already alias-walk
// (letvaris* / the tlt tnode walk) and stay put.
let dti: *tinfo = nil;
if (d.lhs != nil) { dti = tichase(d.lhs.type_: *tinfo); };
// C-t3 (#48): tuple global — slot-laid DATAW
// row (+ DATAR ptr patches for str elements)
// via emittupledata. Unsupported element
@@ -39363,8 +39364,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// below handles it and the duplicate DATAW would
// otherwise differ across stages on user code.
let isarr8: bool = false;
if (d.lhs != nil) {
if (d.lhs.kind == nkind.N_TARRAY) { isarr8 = true; };
if (dti != nil) {
if (dti.kind == tykind.TY_ARRAY) { isarr8 = true; };
};
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup) {
let v: u64 = 0u64;
@@ -39516,13 +39517,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
if (issg) {
if (d.rhs == nil) {
let zsz: i32 = sz;
if (d.lhs != nil) {
let ti: *tinfo = d.lhs.type_: *tinfo;
for (ti != nil && ti.kind == tykind.TY_NAMED) {
ti = ti.under;
};
if (ti != nil) { zsz = ti.size: i32; };
};
if (dti != nil) { zsz = dti.size: i32; };
emitline("DATAW ");
emitsymnamehint(c, nm, d.nmod);
emitline("(SB),\"");
@@ -39544,8 +39539,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// fold-failures don't corrupt the DATA literal.
// No-rhs arrays (e.g. `let buf: [N]u8;`) go through
// the same helper with rhs=nil → zero-fill branch.
if (d.lhs != nil) {
if (d.lhs.kind == nkind.N_TARRAY) {
if (dti != nil) {
if (dti.kind == tykind.TY_ARRAY) {
let rh: *node = d.rhs;
let route: bool = false;
if (rh == nil) { route = true; };
@@ -39555,9 +39550,8 @@ fn emitletdataw(c: *cgen, file: *node) void = {
};
};
if (route) {
let at: *tinfo = d.lhs.type_: *tinfo;
emitarraydata(c, "DATAW", nm,
d.nmod, at, rh);
d.nmod, dti, rh);
};
};
};