wcc_ww/cgen: #92 rhsstructpayload N_STRUCTLIT arm through structlookupchain

The N_STRUCTLIT arm still did bare structlookup on the literal's type
name — an alias-named struct LITERAL widened into a union (`type ali =
base; let v: (void|ali) = ali{...}`) missed the registered structinfo
and fell to the scalar widen arm: word0-only/dropped payload, cs 0 /
ww EXIT 1, byte-id NO (reviewer-B2 find, pre-existing at 486f7f8's
parent). Exactly the class batch-2 c2 closed for the N_IDENT-local arm
of the SAME function; same funnel fix — route through structlookupchain
(cgenutil.ww:1758) and return si.sname so every consumer's re-lookup
hits the REGISTERED name.

One pre-authorized rider: structlookupchain's entry gate accepted only
N_TNAME, but a struct literal's type ref parses as N_IDENT (expression
position, lib/ww/parse/expr.ww builds s.lhs as N_IDENT — OBSERVED).
Widened to `N_TNAME || N_IDENT`; the entry-kind predicate is spelled
twice (the early-return and the chain loop's first iteration — the
loop reassigns cur to aliaslookup results, which are always N_TNAME,
so later iterations are untouched). Consumer census, all 10 call
sites: cgenutil:3187(+this arm; post-commit position), cgenexpr:2927/3006/8775/9011,
cgenstmt:3033 guard on N_TNAME explicitly before calling;
cgenexpr:5756 (etnode = type-spec .lhs), cgenstmt:1978 (arrtn.lhs or
the synthesized #79 N_TNAME), cgenstmt:2695 (cglet n.lhs declared
type spec) pass parsetype products only — parsetype builds zero
N_IDENT nodes (lib/ww/parse/parse.ww). No existing caller can pass
N_IDENT: the widening is strictly accepting-more; existing callers
unmoved. NO second inline chase in the arm (close-by-construction).
Base spellings byte-id: structlookup hits at the chain entry and
si.sname == the literal's own name — same string out, same asm.

Pin: 944_alias_accept_run union_slit_{base_ctl,alias,alias2,order} —
base control holds 0/0; 1/2-level alias + permuted decl order graduate
cs0/ww1-byte-id-NO -> 0/0 byte-id. #63 PROBED post-c3 per the
enrollment rule: does NOT green at this site (8B alias struct-lit
let-init still ww silent-zero-fill exit 1; 16B still loud "aggregate
init from unhandled rhs shape") — its miss is the cgenstmt let-init
dispatch, not rhsstructpayload; documented on task #63, left red, no
row enrolled.

Light gates: test-unit 290 green; sizelint 0; 989 ratchet zero flips;
five-mains NEUTRAL vs master-74195ac scratch on identical inputs +
cs==ww on all five. combined.ww regens ride along (#110).
This commit is contained in:
2026-06-05 23:19:49 +09:00
parent 3e9a6955e7
commit 4adf914f2f
4 changed files with 137 additions and 27 deletions

View File

@@ -48,6 +48,11 @@
* | word0-only; cs half c138605, ww half |
* | F2a batch-2 c2 — graduated K_RUN; |
* | base ctl + #54-bound `as` row loud | 0
* union_slit_* | task #92 (F2a batch-3 c3): alias- |
* | named struct LITERAL into a union — |
* | rhsstructpayload N_STRUCTLIT arm |
* | routed through structlookupchain; |
* | base ctl + 1/2-level + decl order | 0
* nested_alias_field_* | task #71: alias-typed nested-field |
* | store/read/addr-of walks fold to |
* | direct offsets (byte-id graduation, |
@@ -753,6 +758,78 @@ static const struct row rows[] = {
" };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
/* ---- F2a batch-3 c3 (task #92, reviewer-B2 find): the same
* rhsstructpayload widen, sourced from a struct LITERAL instead
* of a local ident — the N_STRUCTLIT arm still did bare
* structlookup, so `ali{...}` into a union fell to the scalar
* widen arm (cs 0 / ww EXIT 1, byte-id NO, pre-existing on
* master at 486f7f8). Graduated by routing the arm through
* structlookupchain (entry gate widened to the literal's N_IDENT
* type-ref leaf — consumer census in the commit body). Values
* distinct per word, LAST word checked. The alias struct-LIT
* LET-INIT zero-fill (task #63) does NOT green at this site —
* probed post-c3, documented there, deliberately no row here. */
{ "union_slit_base_ctl",
"package main;\n"
"type base = struct { a: size, b: size };\n"
"export fn main() i32 = {\n"
" let v: (void | base) = base{a=4000, b=9000};\n"
" match (v) {\n"
" case let s: base => {\n"
" if (s.a != 4000) { return 1; };\n"
" if (s.b != 9000) { return 2; };\n"
" };\n"
" case void => { return 3; };\n"
" };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
{ "union_slit_alias",
"package main;\n"
"type base = struct { a: size, b: size };\n"
"type ali = base;\n"
"export fn main() i32 = {\n"
" let v: (void | ali) = ali{a=4000, b=9000};\n"
" match (v) {\n"
" case let s: ali => {\n"
" if (s.a != 4000) { return 1; };\n"
" if (s.b != 9000) { return 2; };\n"
" };\n"
" case void => { return 3; };\n"
" };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
/* 2-level: the chain walk (structlookupchain's alias loop). */
{ "union_slit_alias2",
"package main;\n"
"type base = struct { a: size, b: size };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"export fn main() i32 = {\n"
" let v: (void | ali2) = ali2{a=4000, b=9000};\n"
" match (v) {\n"
" case let s: ali2 => {\n"
" if (s.a != 4000) { return 1; };\n"
" if (s.b != 9000) { return 2; };\n"
" };\n"
" case void => { return 3; };\n"
" };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
{ "union_slit_order",
"package main;\n"
"export fn main() i32 = {\n"
" let v: (void | ali) = ali{a=4000, b=9000};\n"
" match (v) {\n"
" case let s: ali => {\n"
" if (s.a != 4000) { return 1; };\n"
" if (s.b != 9000) { return 2; };\n"
" };\n"
" case void => { return 3; };\n"
" };\n"
" return 0;\n"
"};\n"
"type ali = base;\n"
"type base = struct { a: size, b: size };\n", 0, K_RUN, NULL },
/* ---- F2a batch-2 c3/B1: exprprimresolved's N_DOT base walk was
* a hand-rolled 2-peel — a 3-level alias base (or ptr-to-2-level)
* left the cast-source width unknowable on wwstage only, so the