From c1386055636d0d315810ca973a6907e8fe162f27 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 5 Jun 2026 11:31:30 +0900 Subject: [PATCH] =?UTF-8?q?w6c/cgen:=20#62=20Layer-2=20(cs=20half)=20?= =?UTF-8?q?=E2=80=94=20widen=20store/push=20su=20chases=20the=20alias=20ch?= =?UTF-8?q?ain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tagged widen's source classify (`su`) single-peeled: a 2-level chain ali->base->struct left su TY_NAMED, so an alias-NAMED struct union member fell past the struct arm to the SCALAR arm — word0-only payload, words 1+ zero-filled. At normal decl order this was BOTH- WRONG-IDENTICAL with wwstage (byte-id YES, gate-blind; F0 m5b_match1 exit 2/2). Two sites, the only widen entries: cg_widen_tagged_store (let/assign/match BP path) and cg_widen_tagged_push (the call-arg twin — surfaced by an F1 probe: fn((void|ali)) arg ran 1/1 both-wrong- identical). The variant TAG still keys on the un-chased st — the member's nominal identity is the alias (cg_tag_for_variant), only the copy-width classify chases. CS-ONLY half: wwstage's twin (rhsstructpayload name-keyed structlookup + its push twin, selfhost/cmd/wcc/cgenutil.ww:3062 vs structlookupchain :1691) lands in F2 per the serial plan — until then these shapes are transiently cs!=ww (was identical-wrong). Bootstrap asm cmp-identical vs a pristine 738d7f4 scratch build on all five main.combined.ww, so the 990-997 byte-id gates are untouched. test: 944_alias_accept_run +6 rows from the banked set /tmp/impl62r_layer2_rows.md — L2-1/2 norm+fwd store, L2-4 3-word width loop (last payload word checked), the push-twin arg row, L2-5 base control (ken's gold invariant), and L2-3 (`v as ali`) pinned at task exit 2/2/2/1 there; 64/64 green here. --- cmd/w6c/cgen.c | 13 +++- test/wcc/944_alias_accept_run.c | 116 ++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index 49e9f2e5..c3de178e 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -2479,7 +2479,13 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src, } } Type *st = src ? src->type : NULL; - Type *su = (st && st->kind == TY_NAMED) ? st->under : st; + /* Transitive chase (#62 Layer-2): a 2-level chain ali->base->struct + * left su TY_NAMED, so the alias-named union member fell past the + * struct arm to the SCALAR store — word0-only payload, words 1+ + * zero-filled (both-wrong-identical with wwstage at normal decl + * order; gate-blind). The tag still keys on the un-chased st: the + * member's nominal identity (cg_tag_for_variant) is the alias. */ + Type *su = type_chase_named(st); /* Tagged → tagged subset: copy slot words then tag-remap. */ if (su && su->kind == TY_TAGGED) { int ssz = (int)su->size; @@ -2894,7 +2900,10 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz) return; } Type *st = src ? src->type : NULL; - Type *su = (st && st->kind == TY_NAMED) ? st->under : st; + /* Transitive chase (#62 Layer-2) — the call-arg twin of the store + * su above: an unchased 2-level alias took the direct-push scalar + * fast arm (word0-only push). */ + Type *su = type_chase_named(st); int src_is_struct = su && su->kind == TY_STRUCT; int src_is_tagged = su && su->kind == TY_TAGGED; /* #66: a tuple-typed source has no direct-push shape — the scalar diff --git a/test/wcc/944_alias_accept_run.c b/test/wcc/944_alias_accept_run.c index 5a00fa1a..37d23157 100644 --- a/test/wcc/944_alias_accept_run.c +++ b/test/wcc/944_alias_accept_run.c @@ -45,6 +45,11 @@ * | struct-lit, walk leaves, reads); | * | unprobed gates LOUD-tripwired (#73, | * | K_BUILDERR_CS pin) | 0 + * union_store_* + | task #62 Layer-2 (cs half): alias- | + * union_push_arg | NAMED struct as union member — widen | + * | store/push word0-only; CS-ONLY until | + * | F2 lands the wwstage half; base ctl | + * | + the #54-bound `as` row pinned loud | 0(cs) * * K_RUN rows also assert cstage/wwstage asm byte-id (acceptance * graduations land byte-identical — F0's accept side was wwstage). @@ -651,6 +656,117 @@ static const struct row rows[] = { " if (take(x) != 26) { return 1; };\n" " return 0;\n" "};\n", 0, K_RUN, NULL }, + /* ---- task #62 Layer-2 (cstage half): alias-NAMED struct as a + * UNION MEMBER. The widen store/push su single peel fell past the + * struct arm to the scalar arm — word0-only payload (the banked + * rows /tmp/impl62r_layer2_rows.md; both-wrong-identical with + * wwstage at normal decl order = gate-blind byte-id). CS-ONLY + * until F2 routes wwstage rhsstructpayload/push through + * structlookupchain — flip these to K_RUN then. Values distinct + * per word; the LAST payload word is always checked. */ + { "union_store_norm", /* banked row L2-1 (= F0 m5b_match1) */ + "package main;\n" + "type base = struct { a: size, b: size };\n" + "type ali = base;\n" + "export fn main() i32 = {\n" + " let x: ali;\n" + " x.a = 4; x.b = 9;\n" + " let v: (void | ali) = x;\n" + " match (v) {\n" + " case let s: ali => {\n" + " if (s.a != 4) { return 1; };\n" + " if (s.b != 9) { return 2; };\n" + " };\n" + " case void => { return 3; };\n" + " };\n" + " return 0;\n" + "};\n", 0, K_RUN_CS, NULL }, /* ww: task #62 F2 */ + { "union_store_fwd", /* banked row L2-2 (= F0 m5_match) */ + "package main;\n" + "type ali = base;\n" + "type base = struct { a: size, b: size };\n" + "export fn main() i32 = {\n" + " let x: ali;\n" + " x.a = 4; x.b = 9;\n" + " let v: (void | ali) = x;\n" + " match (v) {\n" + " case let s: ali => {\n" + " if (s.a != 4) { return 1; };\n" + " if (s.b != 9) { return 2; };\n" + " };\n" + " case void => { return 3; };\n" + " };\n" + " return 0;\n" + "};\n", 0, K_RUN_CS, NULL }, /* ww: task #62 F2 */ + { "union_store_3word", /* banked row L2-4: pins the width LOOP */ + "package main;\n" + "type base = struct { a: size, b: size, c: size };\n" + "type ali = base;\n" + "export fn main() i32 = {\n" + " let x: ali;\n" + " x.a = 4; x.b = 9; x.c = 3;\n" + " let v: (void | ali) = x;\n" + " match (v) {\n" + " case let s: ali => {\n" + " if (s.a != 4) { return 1; };\n" + " if (s.c != 3) { return 2; };\n" + " };\n" + " case void => { return 3; };\n" + " };\n" + " return 0;\n" + "};\n", 0, K_RUN_CS, NULL }, /* ww: task #62 F2 */ + { "union_push_arg", /* the cg_widen_tagged_push twin (F1 find) */ + "package main;\n" + "type base = struct { a: size, b: size };\n" + "type ali = base;\n" + "fn peek(v: (void | ali)) size = {\n" + " match (v) {\n" + " case let s: ali => { return s.b; };\n" + " case void => { return 0; };\n" + " };\n" + "};\n" + "export fn main() i32 = {\n" + " let x: ali;\n" + " x.a = 4; x.b = 9;\n" + " if (peek(x) != 9) { return 1; };\n" + " return 0;\n" + "};\n", 0, K_RUN_CS, NULL }, /* ww: task #62 F2 */ + /* banked row L2-5: the m5b_match0 gold invariant — base spelling + * indistinguishable (ken oracle PART 1.7). */ + { "union_store_base_ctl", + "package main;\n" + "type base = struct { a: size, b: size, c: size };\n" + "export fn main() i32 = {\n" + " let x: base;\n" + " x.a = 4; x.b = 9; x.c = 3;\n" + " let v: (void | base) = x;\n" + " match (v) {\n" + " case let s: base => {\n" + " if (s.a != 4) { return 1; };\n" + " if (s.c != 3) { return 2; };\n" + " };\n" + " case void => { return 3; };\n" + " };\n" + " return 0;\n" + "};\n", 0, K_RUN, NULL }, + /* banked row L2-3 (`v as ali` readback) trips task #54's as-binding + * aggregate-init bound — LOUD both stages today; fold the row into + * #54's pin set on its fold (do not drop). Pinned here as the + * loud-both bound so a silent regression can't hide. */ + { "union_as_bound_54", + "package main;\n" + "type base = struct { a: size, b: size };\n" + "type ali = base;\n" + "export fn main() i32 = {\n" + " let x: ali;\n" + " x.a = 4; x.b = 9;\n" + " let v: (void | ali) = x;\n" + " if (!(v is ali)) { return 1; };\n" + " let w = v as ali;\n" + " if (w.b != 9) { return 2; };\n" + " return 0;\n" + "};\n", 0, K_BUILDERR, + "aggregate init from unhandled rhs shape" }, }; static int