cgen: c2 variant pass-2 structural fallback chases the full NAMED chain — both stages fused

F2a batch-4 c2. Site: cgenutil.ww flatvariantidxt pass-2 (was :2895
at 74195ac, :2903 at 4adf914 post-batch-3) + cs twin cg_tag_for_variant
(cmd/w6c/cgen.c:920-933).

The structural fallback matched a bare source against a NAMED variant
by peeling exactly ONE level (pu.under compare) — a 2-level-alias
variant (type a=*X; type b=a) missed every pass and the widen
defaulted to tag 0, SILENT (the legacy-#17-comment class; that
comment's "task #17" label is retired here — current task #17 is the
unrelated arrlit item).

cs-twin probe DECIDED THE FUSE (spec obligation): v2_alias2 (bare
*i64 into 2-level ptr-alias variant) ran exit 1/1 BOTH-WRONG-IDENTICAL
byte-id pre-fix — cs has the identical single peel, so both stages fix
in this commit (NOT ww-only align-up). NOTE: the cs BINARY is NOT
frozen this train — this commit legitimately moves cstage codegen;
movers must stay bounded to the c2 family (verdict-sweep obligation).
Fix: typeeq/type_eq against tichase/type_chase_named of the variant;
the TY_NAMED gate keeps bare variants in pass-1's exact domain; drew's
>=2-candidate hard-error now guards the CHASED match set (v2_ambig
pins it HOLDING; v2_ambig2 pins the RATIFIED acceptance NARROWING —
2-level twins flip build-accepted-silent-mis-tag -> hard-error BOTH
stages, FLAG-P1/kb4_v2_ambig2). Not nominal-sensitive beyond the
documented proviso — the chase only deepens the structural compare;
nominal choice among >=2 candidates still hard-errors (#209/#211 hold
condition does not trip).

ROUTE-TRACE (rob's §3 ruling) — outcome (b): post-c2 the c4_bool2
shape (2-level bool alias variant, concrete-bool source on ww) runs
0/0 fully BYTE-ID — ww's concrete bool (the #90 stamp divergence,
still open) now reaches the variant through the chased structural
fallback and converges with cs's untyped-funnel route on the same
tag. The graduation therefore belongs to THIS commit: 944
untyped_bool2lvl_bound90 flips K_RUN_CS -> K_RUN here; c5 (#90 stamp
flip) pins its own acceptance rows.

Pin rows (944, all OBSERVED at the c1 base):
  v2_ctrl    bare *T into 1-level NAMED-*T (io vtable shape)
             pre 0/0 byte-id -> post HOLDS (the #15 consumer);
  v2_alias2  bare *T into 2-level alias variant
             pre cs1/ww1 byte-id (both-wrong) -> post 0/0 byte-id;
  v2_struct2 bare anonymous-let struct into 2-level alias variant
             pre cs1/ww1 -> post 0/0 runtime; byte-id SKIPPED via new
             K_RUN_NOID row kind: asm diverges on 3 PRE-EXISTING cglet
             zero-fill lines (cs XORQ+2 stores, ww none — #81 class,
             runtime-correct both, orthogonal to the tag; observed
             identical pre/post). Flip to K_RUN when that closes. The
             adjacent NAMED-source shape is task #95 (ken b4-oracle),
             OUT of this set;
  v2_ambig   bare source matching >=2 NAMED variants (1-level twins)
             -> hard-error HOLDS both stages (K_BUILDERR, diag pinned);
  v2_ambig2  2-LEVEL twin variants — the RATIFIED narrowing pin:
             pre build-ACCEPTED both stages (chase-less fallback
             matched neither twin; silent mis-tag, byte-identical)
             -> post HARD-ERROR both stages (K_BUILDERR, diag pinned).

Corpus: five-mains NEUTRAL vs the c1 build on identical inputs (both
stages — the 2-level variant shape is zero-in-corpus, as the old
comment predicted); cs==ww byte-id holds; 944 181/181.
combined.ww regens ride along.
This commit is contained in:
2026-06-06 00:08:04 +09:00
parent b26b3dbdc5
commit 4e174099bd
5 changed files with 125 additions and 30 deletions

View File

@@ -921,13 +921,18 @@ cg_tag_for_variant(Type *t, Type *vt)
int found = -1, n = 0;
idx = 0;
for (Tparam *p = t->params; p; p = p->next, idx++) {
/* One-level NAMED unwrap: a chained ptr-alias variant
* (type a=*X; type b=a) isn't reached here, so it would
* silently mis-tag — unexercised (zero in corpus), see
* task #17. */
/* Full chase (F2a batch-4 c2): the old one-level
* unwrap missed a chained ptr-alias variant
* (type a=*X; type b=a) — every pass fell through
* and the widen defaulted to tag 0, SILENT. The
* TY_NAMED gate keeps bare variants in pass-1's
* exact domain; the >=2-candidate hard-error below
* guards the CHASED match set. ww twin
* flatvariantidxt fused in this commit (probe:
* both-wrong-identical pre-fix). */
Type *pu = p->type;
if (pu && pu->kind == TY_NAMED && pu->under
&& type_eq(pu->under, vt)) {
if (pu && pu->kind == TY_NAMED
&& type_eq(type_chase_named(pu), vt)) {
if (found < 0) found = idx;
n++;
}

View File

@@ -18973,13 +18973,18 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
let found: i32 = -1;
let n: i32 = 0;
for (q != nil) {
// One-level NAMED unwrap: a chained ptr-alias variant
// (type a=*X; type b=a) isn't reached here, so it would
// silently mis-tag — unexercised (zero in corpus), see
// task #17.
// Full chase (F2a batch-4 c2): the old one-level
// unwrap missed a chained ptr-alias variant
// (type a=*X; type b=a) — every pass fell through
// and the widen defaulted to tag 0, SILENT. The
// TY_NAMED gate keeps bare variants in pass-1's
// exact domain; drew's >=2-candidate hard-error
// below now guards the CHASED match set. cs twin
// cg_tag_for_variant fused in this commit (probe:
// both-wrong-identical pre-fix).
let pu: *tinfo = q.type_;
if (pu != nil && pu.kind == tykind.TY_NAMED
&& pu.under != nil && typeeq(pu.under, want)) {
&& typeeq(tichase(pu), want)) {
if (found < 0) { found = qi; };
n += 1;
};

View File

@@ -2898,13 +2898,18 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
let found: i32 = -1;
let n: i32 = 0;
for (q != nil) {
// One-level NAMED unwrap: a chained ptr-alias variant
// (type a=*X; type b=a) isn't reached here, so it would
// silently mis-tag — unexercised (zero in corpus), see
// task #17.
// Full chase (F2a batch-4 c2): the old one-level
// unwrap missed a chained ptr-alias variant
// (type a=*X; type b=a) — every pass fell through
// and the widen defaulted to tag 0, SILENT. The
// TY_NAMED gate keeps bare variants in pass-1's
// exact domain; drew's >=2-candidate hard-error
// below now guards the CHASED match set. cs twin
// cg_tag_for_variant fused in this commit (probe:
// both-wrong-identical pre-fix).
let pu: *tinfo = q.type_;
if (pu != nil && pu.kind == tykind.TY_NAMED
&& pu.under != nil && typeeq(pu.under, want)) {
&& typeeq(tichase(pu), want)) {
if (found < 0) { found = qi; };
n += 1;
};

View File

@@ -18973,13 +18973,18 @@ fn flatvariantidxt(tagged: *tinfo, want: *tinfo) i32 = {
let found: i32 = -1;
let n: i32 = 0;
for (q != nil) {
// One-level NAMED unwrap: a chained ptr-alias variant
// (type a=*X; type b=a) isn't reached here, so it would
// silently mis-tag — unexercised (zero in corpus), see
// task #17.
// Full chase (F2a batch-4 c2): the old one-level
// unwrap missed a chained ptr-alias variant
// (type a=*X; type b=a) — every pass fell through
// and the widen defaulted to tag 0, SILENT. The
// TY_NAMED gate keeps bare variants in pass-1's
// exact domain; drew's >=2-candidate hard-error
// below now guards the CHASED match set. cs twin
// cg_tag_for_variant fused in this commit (probe:
// both-wrong-identical pre-fix).
let pu: *tinfo = q.type_;
if (pu != nil && pu.kind == tykind.TY_NAMED
&& pu.under != nil && typeeq(pu.under, want)) {
&& typeeq(tichase(pu), want)) {
if (found < 0) { found = qi; };
n += 1;
};

View File

@@ -103,6 +103,8 @@ slurp_eq(const char *a, const char *b)
#define K_BUILDERR 1 /* build FAILS with experr on BOTH drivers */
#define K_RUN_CS 2 /* cstage only — ww half pending (cite at row) */
#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 */
struct row { const char *label; const char *src; int want;
int kind; const char *experr; };
@@ -927,13 +929,12 @@ static const struct row rows[] = {
" if (!(v is ba1)) { return 1; };\n"
" return 0;\n"
"};\n", 0, K_RUN, NULL },
/* BOUND row (task #90): ww stamps N_TRUE CONCRETE bool
* (check.ww:2440) vs cs untyped_bool (check.c:1234), so the bool
* literal never reaches the c4-chased untyped funnel on ww — it
* dies at flatvariantidxt pass-2's one-level pu.under guard
* (cgenutil.ww:2868, the #17 site): SILENT tag 0, ww runs exit 1
* where cs runs 0. Flip to K_RUN when #90 clears BOTH layers
* (stamp + fallback peel). */
/* GRADUATED at F2a batch-4 c2 (route-trace case (b)): the
* pass-2 full chase alone took this 0/0 byte-id — ww's CONCRETE
* bool (the #90 stamp divergence, still open at c2) now matches
* the 2-level alias variant through the chased structural
* fallback, converging with cs's untyped-funnel route on the
* same tag. #90's stamp flip pins its own rows (c5). */
{ "untyped_bool2lvl_bound90",
"package main;\n"
"type bb1 = bool;\n"
@@ -942,7 +943,81 @@ static const struct row rows[] = {
" let v: (void | bb2) = true;\n"
" if (!(v is bb2)) { return 1; };\n"
" return 0;\n"
"};\n", 0, K_RUN_CS, NULL }, /* ww: task #90 */
"};\n", 0, K_RUN, NULL },
/* ---- F2a batch-4 c2: flatvariantidxt/cg_tag_for_variant pass-2
* FULL CHASE, fused both stages (probe: v2_alias2 was exit 1/1
* BOTH-WRONG-IDENTICAL pre-fix — the one-level pu unwrap missed
* every pass on a 2-level alias variant, tag 0 silent). v2_ctrl
* pins the #15 io-vtable consumer (1-level, held throughout);
* v2_ambig pins drew's >=2-candidate hard-error applied to the
* CHASED match set. */
{ "v2_ctrl", /* bare *T into 1-LEVEL named-ptr variant (io shape) */
"package main;\n"
"type pc = *i64;\n"
"export fn main() i32 = {\n"
" let x: i64 = 7;\n"
" let v: (void | pc) = &x;\n"
" if (v is pc) { return 0; };\n"
" return 1;\n"
"};\n", 0, K_RUN, NULL },
{ "v2_alias2", /* bare *T into 2-LEVEL alias variant */
"package main;\n"
"type pa = *i64;\n"
"type pb = pa;\n"
"export fn main() i32 = {\n"
" let x: i64 = 7;\n"
" let v: (void | pb) = &x;\n"
" if (v is pb) { return 0; };\n"
" return 1;\n"
"};\n", 0, K_RUN, NULL },
{ "v2_struct2", /* bare (anonymous-let) struct into 2-level alias
* variant. NOID: asm diverges on 3 pre-existing
* cglet zero-fill lines (cs XORQ+2 stores, ww none
* — task #81 class, runtime-correct both, ORTHOGONAL
* to the variant tag). Flip to K_RUN when the
* zero-fill divergence closes. The adjacent
* NAMED-source shape is task #95 (ken b4-oracle),
* out of this fix's set. */
"package main;\n"
"type sa = struct { a: i64, };\n"
"type sb = sa;\n"
"export fn main() i32 = {\n"
" let s: struct { a: i64, } = sa{a=7};\n"
" let v: (void | sb) = s;\n"
" if (v is sb) { return 0; };\n"
" return 1;\n"
"};\n", 0, K_RUN_NOID, NULL },
{ "v2_ambig", /* bare source matching >=2 NAMED variants stays a
* hard error on the CHASED set (drew's proviso) */
"package main;\n"
"type p1 = *i64;\n"
"type p2 = *i64;\n"
"export fn main() i32 = {\n"
" let x: i64 = 7;\n"
" let v: (void | p1 | p2) = &x;\n"
" if (v is p1) { return 0; };\n"
" return 1;\n"
"};\n", 0, K_BUILDERR,
"bare source structurally matches >=2 NAMED variants" },
/* The RATIFIED acceptance-narrowing pin (FLAG-P1, kb4_v2_ambig2):
* 2-LEVEL twin variants. Pre-c2 the chase-less fallback matched
* NEITHER twin — the build ACCEPTED on both stages and ran a
* silent mis-tag (byte-identical, gate-blind). The chased set
* sees both twins structurally -> drew's >=2-candidate
* hard-error, BOTH stages. */
{ "v2_ambig2",
"package main;\n"
"type q1 = *i64;\n"
"type p1 = q1;\n"
"type q2 = *i64;\n"
"type p2 = q2;\n"
"export fn main() i32 = {\n"
" let x: i64 = 7;\n"
" let v: (void | p1 | p2) = &x;\n"
" if (v is p1) { return 0; };\n"
" return 1;\n"
"};\n", 0, K_BUILDERR,
"bare source structurally matches >=2 NAMED variants" },
/* 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);
@@ -1131,7 +1206,7 @@ main(void)
for (int i = 0; i < n; i++) {
if (rows[i].kind == K_RUN_CS
|| rows[i].kind == K_BUILDERR_CS)
continue;
continue; /* K_RUN_NOID: ww half RUNS */
total++;
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
}