From 10d886906f57d7cf95051cf76a6da0ca99ad5f09 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Sat, 6 Jun 2026 00:12:50 +0900 Subject: [PATCH] wcc_ww/check: c3 spread-member + &len/cap base walks chase the full alias chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F2a batch-4 c3, the check.ww behavior pair (exact set). Both cs twins ALREADY full-chase at this SHA — ww-only align-up, no fuse: B1 (check.ww:2038 census line; peel was at :2041 post-c1): the `...inner` spread member peeled ONE level, so a 2-level-alias inner union escaped the TY_TAGGED splice test and rode as a SURFACE member — the outer box sized off the inner union's own header (probe sp_alias2: runtime 0/0 BOTH but byte-DIVERGE, ww frame $48 vs $32). cs twin chases (check.c:755 type_chase_named, the spec's :660-667 cite moved). Fix: vu = tichase(vu) under the isspread gate, mirroring cs's `spread ? type_chase_named(vt) : vt`. sp_alias2 graduates 0/0 byte-id; sp_alias1 1-level control held throughout. B2 (check.ww:2273/:2275): the &x.len/&x.cap base walk hand-peeled NAMED->PTR->NAMED one level per hop — 2-level alias bases fell out of the slice/str detect and took the generic *opt typing. cs twin chases both hops (check.c:1198-1201). Fix: tichase per hop, exact cs mirror. B2 ROUTE-TRACE (flagged, not edited): the spec's "post 0/0" cell for amplen2 is NOT achievable inside this grant — OBSERVED pre-fix that ww dies LATER at the cgen ADDRESS tail ("unsupported address-of shape") for ALL alias bases 1+ LEVEL (cs runs 0): task #96 (ken b4-oracle), a cgenexpr.ww site outside c3's check.ww set. The chase here aligns the stamped type only (latent until #96); rows pinned LOUD-HOLD via the new K_RUN_CS_WWERR kind (cs builds+runs; the ww build must keep FAILING loud with the pinned diagnostic — a silent ww accept-and-run is the regression the bound row exists to catch), citing #96; they graduate K_RUN with it. Also OBSERVED: the let-init spelling (`let s: sl1 = [1,2,3]`) is blocked further upstream by the rows use the param route. Pin rows (944): sp_alias1 K_RUN hold, sp_alias2 K_RUN graduation, amplen_plain K_RUN control hold, amplen1/amplen2/ampcap2 K_RUN_CS_WWERR LOUD-HOLD bound on #96 (ww leg asserts "unsupported address-of shape"). 944: 196/196. Corpus: five-mains NEUTRAL vs base on identical inputs, cs==ww holds (both clusters zero-in-corpus). combined.ww regens ride along. --- selfhost/cmd/w6c/main.combined.ww | 23 +++--- selfhost/cmd/wcc/check.ww | 23 +++--- selfhost/cmd/wwdump/main.combined.ww | 23 +++--- test/wcc/944_alias_accept_run.c | 101 ++++++++++++++++++++++++++- 4 files changed, 145 insertions(+), 25 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 628882ca..6c994e7c 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -12308,11 +12308,11 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { if (circularnamed(c, vt, v)) { vt = c.tc.tyerr; }; let isspread: bool = (v.op == tkind.TK_ELLIPSIS); let vu: *tinfo = vt; - if (isspread) { - if (vu != nil) { - if (vu.kind == tykind.TY_NAMED) { vu = vu.under; }; - }; - }; + // Full chase (F2a batch-4 c3-B1): the single peel left a + // 2-level-alias inner union a SURFACE member — the box + // sized off the inner union's own header, not its + // spliced variants (cs twin check.c:755 chases). + if (isspread) { vu = tichase(vu); }; if (isspread && vu != nil && vu.kind == tykind.TY_TAGGED) { // #209: a `...inner` spread's PAYLOAD is its // members, not the whole inner union — size/align @@ -12544,10 +12544,17 @@ fn unoptype(c: *checker, e: *node) *node = { if (streq(fld, "len") || streq(fld, "cap")) { let base: *node = e.lhs.lhs; if (base != nil && base.type_ != nil) { - let bu: *tinfo = base.type_: *tinfo; - if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + // Full chase per hop (F2a batch-4 c3-B2): + // the one-peel-per-hop walk lost 2-level + // alias bases out of the slice/str detect + // (cs twin check.c:1198-1201 chases both + // hops). NOTE the ww cgen ADDRESS tail + // stays loud for ALL alias bases (task + // #96) — this aligns the stamped type + // only; rows graduate with #96. + let bu: *tinfo = tichase(base.type_: *tinfo); if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; }; - if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + bu = tichase(bu); if (bu != nil) { if (bu.kind == tykind.TY_SLICE || bu.kind == tykind.TY_STR) { let pp: *node = newnode(nkind.N_TPTR, "", 0, 0); diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 61683e3d..ed0ce600 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -2036,11 +2036,11 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { if (circularnamed(c, vt, v)) { vt = c.tc.tyerr; }; let isspread: bool = (v.op == tkind.TK_ELLIPSIS); let vu: *tinfo = vt; - if (isspread) { - if (vu != nil) { - if (vu.kind == tykind.TY_NAMED) { vu = vu.under; }; - }; - }; + // Full chase (F2a batch-4 c3-B1): the single peel left a + // 2-level-alias inner union a SURFACE member — the box + // sized off the inner union's own header, not its + // spliced variants (cs twin check.c:755 chases). + if (isspread) { vu = tichase(vu); }; if (isspread && vu != nil && vu.kind == tykind.TY_TAGGED) { // #209: a `...inner` spread's PAYLOAD is its // members, not the whole inner union — size/align @@ -2272,10 +2272,17 @@ fn unoptype(c: *checker, e: *node) *node = { if (streq(fld, "len") || streq(fld, "cap")) { let base: *node = e.lhs.lhs; if (base != nil && base.type_ != nil) { - let bu: *tinfo = base.type_: *tinfo; - if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + // Full chase per hop (F2a batch-4 c3-B2): + // the one-peel-per-hop walk lost 2-level + // alias bases out of the slice/str detect + // (cs twin check.c:1198-1201 chases both + // hops). NOTE the ww cgen ADDRESS tail + // stays loud for ALL alias bases (task + // #96) — this aligns the stamped type + // only; rows graduate with #96. + let bu: *tinfo = tichase(base.type_: *tinfo); if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; }; - if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + bu = tichase(bu); if (bu != nil) { if (bu.kind == tykind.TY_SLICE || bu.kind == tykind.TY_STR) { let pp: *node = newnode(nkind.N_TPTR, "", 0, 0); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index a9bd2796..d4ea2165 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -12308,11 +12308,11 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { if (circularnamed(c, vt, v)) { vt = c.tc.tyerr; }; let isspread: bool = (v.op == tkind.TK_ELLIPSIS); let vu: *tinfo = vt; - if (isspread) { - if (vu != nil) { - if (vu.kind == tykind.TY_NAMED) { vu = vu.under; }; - }; - }; + // Full chase (F2a batch-4 c3-B1): the single peel left a + // 2-level-alias inner union a SURFACE member — the box + // sized off the inner union's own header, not its + // spliced variants (cs twin check.c:755 chases). + if (isspread) { vu = tichase(vu); }; if (isspread && vu != nil && vu.kind == tykind.TY_TAGGED) { // #209: a `...inner` spread's PAYLOAD is its // members, not the whole inner union — size/align @@ -12544,10 +12544,17 @@ fn unoptype(c: *checker, e: *node) *node = { if (streq(fld, "len") || streq(fld, "cap")) { let base: *node = e.lhs.lhs; if (base != nil && base.type_ != nil) { - let bu: *tinfo = base.type_: *tinfo; - if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + // Full chase per hop (F2a batch-4 c3-B2): + // the one-peel-per-hop walk lost 2-level + // alias bases out of the slice/str detect + // (cs twin check.c:1198-1201 chases both + // hops). NOTE the ww cgen ADDRESS tail + // stays loud for ALL alias bases (task + // #96) — this aligns the stamped type + // only; rows graduate with #96. + let bu: *tinfo = tichase(base.type_: *tinfo); if (bu != nil && bu.kind == tykind.TY_PTR) { bu = bu.sub; }; - if (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; }; + bu = tichase(bu); if (bu != nil) { if (bu.kind == tykind.TY_SLICE || bu.kind == tykind.TY_STR) { let pp: *node = newnode(nkind.N_TPTR, "", 0, 0); diff --git a/test/wcc/944_alias_accept_run.c b/test/wcc/944_alias_accept_run.c index 4ecfd5bd..3e344a92 100644 --- a/test/wcc/944_alias_accept_run.c +++ b/test/wcc/944_alias_accept_run.c @@ -61,7 +61,11 @@ * K_RUN rows also assert cstage/wwstage asm byte-id (acceptance * graduations land byte-identical — F0's accept side was wwstage). * K_RUN_CS / K_BUILDERR_CS rows run the cstage driver only, each - * citing the task that graduates them. binop_alias_vs_alias is + * citing the task that graduates them. K_RUN_CS_WWERR rows also run + * the wwstage driver expecting a LOUD build failure (the bound row's + * job is preventing a silent ww regression while the leg is parked); + * K_RUN_NOID rows run both but waive the byte-id cell, citing the + * blocking task at the row. binop_alias_vs_alias is * CS-only loud: the wwstage checker stays silent on typed mismatches * by design (5-lite discipline) — cs pins the harec-parity reject. * NNN<950, self-contained (/tmp, no imports) — rule-14's selfhost- @@ -105,6 +109,9 @@ slurp_eq(const char *a, const char *b) #define K_BUILDERR_CS 3 /* cstage-only loud (ww silent by design) */ #define K_RUN_NOID 4 /* build+run BOTH, exit==want, byte-id * SKIPPED — cite the blocking task */ +#define K_RUN_CS_WWERR 5 /* LOUD-HOLD bound row: cstage build+run, + * exit==want; WWSTAGE build must FAIL + * with experr — cite the blocking task */ struct row { const char *label; const char *src; int want; int kind; const char *experr; }; @@ -1018,6 +1025,87 @@ static const struct row rows[] = { " return 1;\n" "};\n", 0, K_BUILDERR, "bare source structurally matches >=2 NAMED variants" }, + /* ---- F2a batch-4 c3: check.ww behavior pair (ww-only align-up; + * both cs twins already full-chase: spread check.c:755, &len/cap + * check.c:1198-1201). */ + { "sp_alias1", /* 1-level spread control — held throughout */ + "package main;\n" + "type inner = (i64 | str);\n" + "type outer = (...inner | void);\n" + "export fn main() i32 = {\n" + " let v: outer = 5: i64;\n" + " if (v is i64) { return 0; };\n" + " return 1;\n" + "};\n", 0, K_RUN, NULL }, + { "sp_alias2", /* 2-level-alias inner union spread into outer + * tagged: pre = runtime 0/0 BUT byte-DIVERGE (ww + * sized the box off the SURFACE member — $48 vs $32 + * frame); post = 0/0 byte-id (spliced). */ + "package main;\n" + "type inner = (i64 | str);\n" + "type inner2 = inner;\n" + "type outer = (...inner2 | void);\n" + "export fn main() i32 = {\n" + " let v: outer = 5: i64;\n" + " if (v is i64) { return 0; };\n" + " return 1;\n" + "};\n", 0, K_RUN, NULL }, + { "amplen_plain", /* &s.len plain []i64 base control */ + "package main;\n" + "export fn main() i32 = {\n" + " let s: []i64 = [1, 2, 3];\n" + " let p: *i64 = &s.len;\n" + " if (*p == 3) { return 0; };\n" + " return 1;\n" + "};\n", 0, K_RUN, NULL }, + /* &len/cap through alias bases (param route — the let-init + * spelling is blocked upstream by the #83-documented "let: not + * assignable" acceptance divergence): the c3-B2 chase aligns the + * CHECKER stamp to cs, but the ww cgen ADDRESS tail stays loud + * ("unsupported address-of shape") for ALL alias bases 1+ level — + * task #96 (ken b4-oracle), outside check.ww's grant. Rows + * graduate K_RUN when #96 lands. */ + { "amplen1_bound96", + "package main;\n" + "type sl1 = []i64;\n" + "fn check(s: sl1) i32 = {\n" + " let p: *i64 = &s.len;\n" + " if (*p == 3) { return 0; };\n" + " return 1;\n" + "};\n" + "export fn main() i32 = {\n" + " let b: []i64 = [1, 2, 3];\n" + " return check(b);\n" + "};\n", 0, K_RUN_CS_WWERR, + "unsupported address-of shape" }, /* ww: task #96 */ + { "amplen2_bound96", + "package main;\n" + "type sl1 = []i64;\n" + "type sl2 = sl1;\n" + "fn check(s: sl2) i32 = {\n" + " let p: *i64 = &s.len;\n" + " if (*p == 3) { return 0; };\n" + " return 1;\n" + "};\n" + "export fn main() i32 = {\n" + " let b: []i64 = [1, 2, 3];\n" + " return check(b);\n" + "};\n", 0, K_RUN_CS_WWERR, + "unsupported address-of shape" }, /* ww: task #96 */ + { "ampcap2_bound96", + "package main;\n" + "type sl1 = []i64;\n" + "type sl2 = sl1;\n" + "fn check(s: sl2) i32 = {\n" + " let p: *i64 = &s.cap;\n" + " if (*p == 3) { return 0; };\n" + " return 1;\n" + "};\n" + "export fn main() i32 = {\n" + " let b: []i64 = [1, 2, 3];\n" + " return check(b);\n" + "};\n", 0, K_RUN_CS_WWERR, + "unsupported address-of shape" }, /* ww: task #96 */ /* BOUND expected-state row (#199α + #90): untyped literal into a * NESTED-tagged alias-wrapped variant. cs CHECKER loud-rejects — * the #199α ww-stricter no-transitive-drill rule (type.c:316-324); @@ -1207,6 +1295,17 @@ main(void) if (rows[i].kind == K_RUN_CS || rows[i].kind == K_BUILDERR_CS) continue; /* K_RUN_NOID: ww half RUNS */ + if (rows[i].kind == K_RUN_CS_WWERR) { + /* LOUD-HOLD: the parked ww leg must keep + * failing LOUD with experr — a silent ww + * accept-and-run is the regression this + * row exists to catch (cite at row). */ + struct row tmp = rows[i]; + tmp.kind = K_BUILDERR; + total++; + if (run_driver(wdrv, &tmp, i) != 0) fail++; + continue; + } total++; if (run_driver(wdrv, &rows[i], i) != 0) fail++; }