test: migrate Fam9 match/tagged value tests to @test (#5-C5)

fold-2 chunk C5 (drew's Fam8-13 plan), the highest-risk chunk: 21 match/tagged
value-row C drivers re-homed. 20 -> test/lang/*_test.ww @test row-tables + 12
runww //ww:error carriers (both stages reject). The global-tag cluster
(globtag*/globstructwiden/taggedderefstore/...), which sits on the #15/#17
global-ptr fix, was empirically probed byte-id CLEAN -- the predicted hotspot
surfaced ZERO fresh cs!=ww. Carves: variant_chain_b95 #81 -> _runonly (genuinely
diverges at HEAD); callret_bound277 #277 -> slim C pin (cs-runs/ww-rejects),
mutation-gated. 929_tagged_memarg kept whole (SSE-ABI asm conformance). 19
drivers deleted, 944_variant_chain slimmed to the #277 pin.

The match-on-tagged-struct-field divergence (former #26) probed RESOLVED for all
its cited shapes (938 voidstr_field/recursion_torture, tagnorm dedup_match all
byte-id cs==ww + value-correct) -- closed no-reproducer-at-HEAD, attribution to
#15/#17 INFERRED. Those rows migrate as normal byte-id @test and serve as the
REGRESSION SENTINEL for the inferred close (a resurgence trips the gate).

LANGBYTEID floor 93->113; test count 374->355 (19 deleted; 929 + 944_variant_chain
kept). do-not-auto-batch (926_tagscr/940_global_sret/940_str_forrange) untouched.
This commit is contained in:
2026-06-24 04:27:00 +09:00
parent 60dec4a6bf
commit 246e5bb90e
54 changed files with 1955 additions and 5820 deletions

View File

@@ -1,238 +0,0 @@
/*
* 924_tagged_call_arg_run — wwstage caller must not drop the DX/CX/R8
* payload words when passing a tagged-union arg sourced from a fn
* call that returns the same tagged union (task #21).
*
* Pre-fix the wwstage call-arg-emit dispatch in pushargsrev only
* recognized N_IDENT as an already-tagged source. For arg.kind ==
* N_CALL whose callee returns the param's tagged type, the widening
* path treated AX (the returned tag word) as a concrete-variant
* payload, then clobbered AX with widentag for the tag-push slot.
* The DX/CX/R8 value-words from the tagged-return ABI were silently
* dropped — the callee read its second arg-reg (SI / DX / etc.) as
* 0 or as the original tag.
*
* Cstage already had the right shape: a `type_eq(p->type, at)` guard
* at cmd/w6c/cgen.c:4216-4221 sets widen[i]=0, then the natural-push
* branch at 4373-4387 pushes R8/CX/DX/AX high→low. Fix aligns
* wwstage DOWN to that shape (rule 10):
* 1. pushargsrev's aistagged guard (cgenutil.ww) now also fires
* when taggedcallslot(arg) == slotsize(ptype), matching cstage.
* 2. The natural-push fallthrough adds a taggedcallslot arm that
* pushes R8/CX/DX/AX high→low by slot size.
* 3. cgcall's per-arg pop-count uses taggedcallslot too so the
* next arg's POPQ doesn't land on residual tagged words.
*
* Rows pin:
* - 4-variant (rune | done | more | invalid) CALL-source: the
* original utf8 iterator shape from worker-encoding's probe.
* Two-word tagged (16B slot), CALL→CALL composition.
* - IDENT-source guard: a parallel test that was already correct
* pre-fix, pinning that the IDENT fast-path stays byte-identical
* to cstage and isn't perturbed by the fix.
* - 2-variant `(*u8 | oserror)` sanity: narrower 16B slot variant
* to ensure the natural-push arm doesn't regress smaller-slot
* shapes.
* - Multi-arg sequence: tagged-CALL arg followed by a scalar arg,
* pinning that the pop-count fix keeps subsequent args in the
* right register class.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row { const char *label; const char *src; int want; };
static const struct row rows[] = {
{ "4variant_call_source_rune",
"type done = !void; type more = !void; type invalid = !void;\n"
"fn yield_rune() (rune | done | more | invalid) = {\n"
" return 65u32: rune;\n"
"};\n"
"fn dispatch(v: (rune | done | more | invalid)) i32 = {\n"
" match (v) {\n"
" case let r: rune => return r: i32;\n"
" case let d: done => return -1;\n"
" case let m: more => return -2;\n"
" case let e: invalid => return -3;\n"
" };\n"
" return -99;\n"
"};\n"
"export fn main() i32 = {\n"
" if (dispatch(yield_rune()) != 65) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
{ "4variant_ident_source_rune",
"type done = !void; type more = !void; type invalid = !void;\n"
"fn dispatch(v: (rune | done | more | invalid)) i32 = {\n"
" match (v) {\n"
" case let r: rune => return r: i32;\n"
" case let d: done => return -1;\n"
" case let m: more => return -2;\n"
" case let e: invalid => return -3;\n"
" };\n"
" return -99;\n"
"};\n"
"export fn main() i32 = {\n"
" let v: (rune | done | more | invalid) = 65u32: rune;\n"
" if (dispatch(v) != 65) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
{ "2variant_call_source_ptr",
"type oserror = !i32;\n"
"fn yield_ptr() (*u8 | oserror) = {\n"
" let p: *u8 = nil;\n"
" return p;\n"
"};\n"
"fn dispatch(v: (*u8 | oserror)) i32 = {\n"
" match (v) {\n"
" case let p: *u8 => return 7;\n"
" case let e: oserror => return e: i32;\n"
" };\n"
" return -99;\n"
"};\n"
"export fn main() i32 = {\n"
" if (dispatch(yield_ptr()) != 7) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
{ "2variant_call_source_err",
"type oserror = !i32;\n"
"fn yield_err() (*u8 | oserror) = {\n"
" let e: oserror = 42i32: oserror;\n"
" return e;\n"
"};\n"
"fn dispatch(v: (*u8 | oserror)) i32 = {\n"
" match (v) {\n"
" case let p: *u8 => return 7;\n"
" case let e: oserror => return e: i32;\n"
" };\n"
" return -99;\n"
"};\n"
"export fn main() i32 = {\n"
" if (dispatch(yield_err()) != 42) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
/* Multi-arg shape: tagged-CALL followed by a scalar i32 arg.
* Pre-fix the pop-count would be wrong (1 word for arg0 instead
* of 2), leaving arg1's scalar to pick up the residual tag word
* off the stack and routing the real scalar into DX. Post-fix
* the per-arg word count matches the push. */
{ "tagged_call_then_scalar",
"type oserror = !i32;\n"
"fn yield_ptr() (*u8 | oserror) = {\n"
" let p: *u8 = nil;\n"
" return p;\n"
"};\n"
"fn use2(v: (*u8 | oserror), k: i32) i32 = {\n"
" match (v) {\n"
" case let p: *u8 => return k + 1;\n"
" case let e: oserror => return k - 1;\n"
" };\n"
" return -99;\n"
"};\n"
"export fn main() i32 = {\n"
" if (use2(yield_ptr(), 41) != 42) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tca_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/tca_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/tca_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s",
driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char wdrv[640];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr, "tagged_call_arg_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"tagged_call_arg_run[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
if (fail) {
fprintf(stderr,
"tagged_call_arg_run: %d/%d fixtures failed\n", fail, total);
return 1;
}
printf("tagged_call_arg_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,330 +0,0 @@
/*
* 925_nested_union_widen_run — widening a NAMED multi-variant union
* value into an ENCLOSING union must tag the OUTER variant correctly
* and lay the inner tagged value out as the payload at slot+8 (project
* #218).
*
* Gate-blind hazard: the outer-widen of a NAMED multi-variant union
* (`inner`) into `(size | inner)` took the tagged-SUBSET path — it
* byte-copied the inner value at slot+0 and remapped its SUB-variants,
* collapsing every inner sub-variant onto outer tag 0. The match
* EXTRACT side already read the inner value from slot+8 (nested layout),
* so store and extract disagreed and the outer arm mis-selected. The
* byte-id (990-997) and cs==ww asm gates both passed because the
* bootstrap never widens a multi-variant union into an enclosing one;
* the #94 io eFinal error path (`let e: io.error = <leaf>; return e`)
* is the first to exercise it. cstage cg_widen_tagged_store +
* cg_variant_match (cmd/w6c/cgen.c) and the wwstage twin
* (selfhost/cmd/wcc/cgenutil.ww cgwidentaggedstorebp + cgvariantmatch)
* now detect the nested-variant case (the source's whole tagged type
* matches one outer variant — recovered via a structural fallback after
* the nominal-lossy collapse, project tinfo_lossy_nominal) and store
* the inner value at slot+8 with the outer tag at slot+0.
*
* Two assertions per RUN row:
* - RUNTIME: build with both `ww` (cstage) and `ww_ww` (wwstage),
* run, compare the exit code. This is what was wrong pre-fix.
* - ASM BYTE-ID: compile through `w6c` and `w6c_ww` and require
* byte-identical .s (rule-10).
*
* ERR rows assert a COMPILE-ERROR on both stages (the drew collision
* guard): when ≥2 outer variants are structurally identical, the
* structural fallback cannot disambiguate them once nominal identity
* is lost (#199b/B-full), so the compiler must STOP rather than silently
* mis-tag. The over-match is unreachable under today's nominal-lossy
* model but INVERTS when #199b lands — the guard pins the invariant now.
*
* Rows:
* 1. outer_arm_select — let-widen `e: inner` into `(size | inner)`;
* match selects the `inner` arm (the bug returned the `size` arm).
* 2. destructure_return — `return e` widens through the tagged-return
* ABI; the caller destructures the inner i32 payload (5) — proves
* the payload survives the OUTER round-trip (the rule-7 gate: if
* this fails it is the deep wrapped-slot-layout, not this fix).
* 3. destructure_let — let-widen + nested match unwraps the inner i32
* payload (7) — the payload survives a non-return widen too.
* 4. single_variant — scalar widen into `(i32 | bool)`; the non-nested
* single-variant path is unperturbed (control + byte-id witness).
* 5. collision_guard — two structurally-identical NAMED unions as
* sibling variants `(a | b)`; widening must COMPILE-ERROR (the
* guard), not silently pick one.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
/* experr != 0: the source must FAIL to compile on both stages. */
struct row { const char *label; const char *src; int want; int experr; };
static const struct row rows[] = {
{ "outer_arm_select",
"type inner = !(i32|bool);\n"
"export fn main() i32 = {\n"
" let e: inner = (true: inner);\n"
" let r: (size | inner) = e;\n"
" match (r) {\n"
" case let n: size => return 50;\n"
" case let x: inner => return 0;\n"
" };\n"
"};\n",
0, 0 },
{ "destructure_return",
"type inner = !(i32|bool);\n"
"fn g() (size | inner) = {\n"
" let e: inner = (5i32: inner);\n"
" return e;\n"
"};\n"
"export fn main() i32 = {\n"
" match (g()) {\n"
" case let s: size => return 90;\n"
" case let x: inner => match (x) {\n"
" case let i: i32 => return i;\n"
" case let b: bool => return 99;\n"
" };\n"
" };\n"
"};\n",
5, 0 },
{ "destructure_let",
"type inner = !(i32|bool);\n"
"export fn main() i32 = {\n"
" let e: inner = (7i32: inner);\n"
" let r: (size | inner) = e;\n"
" match (r) {\n"
" case let n: size => return 50;\n"
" case let x: inner => match (x) {\n"
" case let i: i32 => return i;\n"
" case let b: bool => return 88;\n"
" };\n"
" };\n"
"};\n",
7, 0 },
{ "single_variant",
"export fn main() i32 = {\n"
" let r: (i32 | bool) = 7i32;\n"
" match (r) {\n"
" case let n: i32 => return n;\n"
" case let b: bool => return 88;\n"
" };\n"
"};\n",
7, 0 },
{ "collision_guard",
"type a = !(i32|bool);\n"
"type b = !(i32|bool);\n"
"export fn main() i32 = {\n"
" let x: a = (1i32: a);\n"
" let r: (a | b) = x;\n"
" match (r) {\n"
" case let p: a => return 1;\n"
" case let q: b => return 2;\n"
" };\n"
"};\n",
0, 1 },
};
/* Write r->src to <dir>/<base>.ww; returns 0 on success. */
static int
write_src(const char *dir, const char *base, const struct row *r, char *out,
size_t outsz)
{
snprintf(out, outsz, "%s/%s.ww", dir, base);
FILE *f = fopen(out, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
return 0;
}
/* Build src with `driver build` inside its own subdir, run the binary,
* return its exit code (or -1 on a build/exec failure). */
static int
build_run(const char *driver, const char *src, const char *workdir)
{
char cmd[8192];
snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1",
workdir, driver, src);
if (runwait(cmd) != 0) return -1;
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
char outbin[1024];
snprintf(outbin, sizeof outbin, "%s/%s", workdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
return runwait(outbin);
}
static int
files_equal(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int ca, cb, eq = 1;
do {
ca = fgetc(fa);
cb = fgetc(fb);
if (ca != cb) { eq = 0; break; }
} while (ca != EOF);
fclose(fa);
fclose(fb);
return eq;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640], wdrv[640], cw6[640], ww6[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
int have_w6cww = (access(ww6, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char dir[] = "/tmp/nuw.XXXXXX";
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label);
fail++; total++;
continue;
}
char src[1024];
if (write_src(dir, "p", r, src, sizeof src) != 0) {
fprintf(stderr, "row[%s]: write src failed\n", r->label);
fail++; total++;
goto cleanup;
}
if (r->experr) {
/* Collision guard: w6c (and w6c_ww) must REFUSE to
* emit. A successful emit is the silent mis-tag the
* guard exists to stop. */
char css[1024], wss[1024], cmd[8192];
snprintf(css, sizeof css, "%s/cs.s", dir);
snprintf(wss, sizeof wss, "%s/ww.s", dir);
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
cw6, css, src);
total++;
if (runwait(cmd) == 0) {
fprintf(stderr,
"row[%s][cstage]: emit SUCCEEDED, "
"want compile-error (#218/#199b)\n",
r->label);
fail++;
}
if (have_w6cww) {
snprintf(cmd, sizeof cmd,
"%s -o %s %s > /dev/null 2>&1",
ww6, wss, src);
total++;
if (runwait(cmd) == 0) {
fprintf(stderr,
"row[%s][wwstage]: emit SUCCEEDED, "
"want compile-error (#218/#199b)\n",
r->label);
fail++;
}
}
goto cleanup;
}
/* ASM byte-id: w6c vs w6c_ww .s must be identical. */
if (have_w6cww) {
char css[1024], wss[1024], cmd[8192];
snprintf(css, sizeof css, "%s/cs.s", dir);
snprintf(wss, sizeof wss, "%s/ww.s", dir);
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
cw6, css, src);
int rc1 = runwait(cmd);
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
ww6, wss, src);
int rc2 = runwait(cmd);
total++;
if (rc1 != 0 || rc2 != 0) {
fprintf(stderr,
"row[%s]: w6c/w6c_ww emit failed (%d/%d)\n",
r->label, rc1, rc2);
fail++;
} else if (files_equal(css, wss) != 1) {
fprintf(stderr,
"row[%s]: cs.s != ww.s (rule-10 break)\n",
r->label);
fail++;
}
}
/* RUNTIME: cstage. */
{
char wk[1024];
snprintf(wk, sizeof wk, "%s/cs", dir);
mkdir(wk, 0755);
int got = build_run(cdrv, src, wk);
total++;
if (got != r->want) {
fprintf(stderr,
"row[%s][cstage]: exit=%d want=%d\n",
r->label, got, r->want);
fail++;
}
}
/* RUNTIME: wwstage (the second half of rule-10). */
if (have_ww) {
char wk[1024];
snprintf(wk, sizeof wk, "%s/ww", dir);
mkdir(wk, 0755);
int got = build_run(wdrv, src, wk);
total++;
if (got != r->want) {
fprintf(stderr,
"row[%s][wwstage]: exit=%d want=%d\n",
r->label, got, r->want);
fail++;
}
}
cleanup:
{
char rm[1100];
snprintf(rm, sizeof rm, "rm -rf %s", dir);
runwait(rm);
}
}
if (fail) {
fprintf(stderr,
"nested_union_widen_run: %d/%d checks failed\n", fail, total);
return 1;
}
printf("nested_union_widen_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,330 +0,0 @@
/*
* 925_tryprop_tag_remap_run — the `?` try operator must remap the
* operand union's error-variant tag into the ENCLOSING fn return
* union's variant order before propagating (project #173).
*
* Gate-blind hazard: when the `?` operand union and the enclosing fn
* return union differ in variant ORDER, the raw operand tag names the
* WRONG variant in the return union. Pre-fix wwstage cgtryprop did NO
* remap (selfhost/cmd/wcc/cgenexpr.ww) and propagated the raw tag, so
* the caller saw the wrong error variant at runtime — while the
* byte-id (990-997) and cs==ww asm gates both passed, because the
* bootstrap only ever tries SAME-order unions. cstage already remapped
* (cmd/w6c/cgen.c:6161-6184); the fix ports that loop into wwstage.
*
* Two assertions per row:
* - RUNTIME: build with both `ww` (cstage) and `ww_ww` (wwstage),
* run, compare the exit code (= which variant the caller matched).
* This is what was wrong pre-fix.
* - ASM BYTE-ID: compile the same source through `w6c` and `w6c_ww`
* and require byte-identical .s. Proves rule-10 (symmetric stages)
* holds AFTER the fix on differing-order too, and that same-order
* emits no remap (zero delta).
*
* Rows:
* 1. diff_order_e1 — g→e1 on a branched callee; f's union swaps the
* error order; caller asserts the e1 arm. Pre-fix wwstage = 2.
* 2. diff_order_e2 — g→e2; caller asserts the e2 arm. Proves BOTH
* error variants remap (not just the first).
* 3. diff_order_success — g→value; caller asserts the i32 success
* unwrap survives unperturbed by the remap.
* 4. same_order — f's union same order as g; the only shape the
* bootstrap exercises; byte-id witness that the remap is inert.
* 5. multiword — differing-order propagation of a MULTI-WORD `!str`
* error (payload in DX/CX/R8). Caller asserts the PAYLOAD BYTES
* ("AB") survive, not just the tag — proves the remap touches
* only AX and the payload regs ride the RET.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row { const char *label; const char *src; int want; };
static const struct row rows[] = {
{ "diff_order_e1",
"type e1 = !void;\n"
"type e2 = !void;\n"
"fn g(which: i32) (i32 | e1 | e2) = {\n"
" if (which == 1) { return e1{}; };\n"
" if (which == 2) { return e2{}; };\n"
" return 100;\n"
"};\n"
"fn f(which: i32) (i32 | e2 | e1) = {\n"
" let v: i32 = g(which)?;\n"
" return v + 1;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f(1)) {\n"
" case let v: i32 => return 50;\n"
" case e2 => return 2;\n"
" case e1 => return 1;\n"
" };\n"
" return 99;\n"
"};\n",
1 },
{ "diff_order_e2",
"type e1 = !void;\n"
"type e2 = !void;\n"
"fn g(which: i32) (i32 | e1 | e2) = {\n"
" if (which == 1) { return e1{}; };\n"
" if (which == 2) { return e2{}; };\n"
" return 100;\n"
"};\n"
"fn f(which: i32) (i32 | e2 | e1) = {\n"
" let v: i32 = g(which)?;\n"
" return v + 1;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f(2)) {\n"
" case let v: i32 => return 50;\n"
" case e2 => return 2;\n"
" case e1 => return 1;\n"
" };\n"
" return 99;\n"
"};\n",
2 },
{ "diff_order_success",
"type e1 = !void;\n"
"type e2 = !void;\n"
"fn g(which: i32) (i32 | e1 | e2) = {\n"
" if (which == 1) { return e1{}; };\n"
" if (which == 2) { return e2{}; };\n"
" return 100;\n"
"};\n"
"fn f(which: i32) (i32 | e2 | e1) = {\n"
" let v: i32 = g(which)?;\n"
" return v + 1;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f(0)) {\n"
" case let v: i32 => { if (v != 101) { return 40; }; return 7; };\n"
" case e2 => return 2;\n"
" case e1 => return 1;\n"
" };\n"
" return 99;\n"
"};\n",
7 },
{ "same_order",
"type e1 = !void;\n"
"type e2 = !void;\n"
"fn g(which: i32) (i32 | e1 | e2) = {\n"
" if (which == 1) { return e1{}; };\n"
" if (which == 2) { return e2{}; };\n"
" return 100;\n"
"};\n"
"fn f(which: i32) (i32 | e1 | e2) = {\n"
" let v: i32 = g(which)?;\n"
" return v + 1;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f(1)) {\n"
" case let v: i32 => return 50;\n"
" case e1 => return 1;\n"
" case e2 => return 2;\n"
" };\n"
" return 99;\n"
"};\n",
1 },
{ "multiword",
"type e1 = !void;\n"
"type emsg = !str;\n"
"fn g(which: i32) (i32 | e1 | emsg) = {\n"
" if (which == 1) { return e1{}; };\n"
" if (which == 2) { let m: emsg = \"AB\": emsg; return m; };\n"
" return 100;\n"
"};\n"
"fn f(which: i32) (i32 | emsg | e1) = {\n"
" let v: i32 = g(which)?;\n"
" return v + 1;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f(2)) {\n"
" case let v: i32 => return 50;\n"
" case let s: emsg => {\n"
" if (s.len != 2) { return 30; };\n"
" if (s[0] != 65u8) { return 31; };\n"
" if (s[1] != 66u8) { return 32; };\n"
" return 7;\n"
" };\n"
" case e1 => return 1;\n"
" };\n"
" return 99;\n"
"};\n",
7 },
};
/* Write r->src to <dir>/<base>.ww; returns 0 on success. */
static int
write_src(const char *dir, const char *base, const struct row *r, char *out,
size_t outsz)
{
snprintf(out, outsz, "%s/%s.ww", dir, base);
FILE *f = fopen(out, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
return 0;
}
/* Build src with `driver build` inside its own subdir, run the binary,
* return its exit code (or -1 on a build/exec failure). */
static int
build_run(const char *driver, const char *src, const char *workdir)
{
char cmd[8192];
snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1",
workdir, driver, src);
if (runwait(cmd) != 0) return -1;
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
char outbin[1024];
snprintf(outbin, sizeof outbin, "%s/%s", workdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
return runwait(outbin);
}
static int
files_equal(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int ca, cb, eq = 1;
do {
ca = fgetc(fa);
cb = fgetc(fb);
if (ca != cb) { eq = 0; break; }
} while (ca != EOF);
fclose(fa);
fclose(fb);
return eq;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640], wdrv[640], cw6[640], ww6[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
int have_w6cww = (access(ww6, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char dir[] = "/tmp/tprm.XXXXXX";
if (mkdtemp(dir) == NULL) {
fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label);
fail++; total++;
continue;
}
char src[1024];
if (write_src(dir, "p", r, src, sizeof src) != 0) {
fprintf(stderr, "row[%s]: write src failed\n", r->label);
fail++; total++;
goto cleanup;
}
/* ASM byte-id: w6c vs w6c_ww .s must be identical. */
if (have_w6cww) {
char css[1024], wss[1024], cmd[8192];
snprintf(css, sizeof css, "%s/cs.s", dir);
snprintf(wss, sizeof wss, "%s/ww.s", dir);
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
cw6, css, src);
int rc1 = runwait(cmd);
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
ww6, wss, src);
int rc2 = runwait(cmd);
total++;
if (rc1 != 0 || rc2 != 0) {
fprintf(stderr,
"row[%s]: w6c/w6c_ww emit failed (%d/%d)\n",
r->label, rc1, rc2);
fail++;
} else if (files_equal(css, wss) != 1) {
fprintf(stderr,
"row[%s]: cs.s != ww.s (rule-10 break)\n",
r->label);
fail++;
}
}
/* RUNTIME: cstage. */
{
char wk[1024];
snprintf(wk, sizeof wk, "%s/cs", dir);
mkdir(wk, 0755);
int got = build_run(cdrv, src, wk);
total++;
if (got != r->want) {
fprintf(stderr,
"row[%s][cstage]: exit=%d want=%d\n",
r->label, got, r->want);
fail++;
}
}
/* RUNTIME: wwstage (the side #173 fixes). */
if (have_ww) {
char wk[1024];
snprintf(wk, sizeof wk, "%s/ww", dir);
mkdir(wk, 0755);
int got = build_run(wdrv, src, wk);
total++;
if (got != r->want) {
fprintf(stderr,
"row[%s][wwstage]: exit=%d want=%d\n",
r->label, got, r->want);
fail++;
}
}
cleanup:
{
char rm[1100];
snprintf(rm, sizeof rm, "rm -rf %s", dir);
runwait(rm);
}
}
if (fail) {
fprintf(stderr,
"tryprop_tag_remap_run: %d/%d checks failed\n", fail, total);
return 1;
}
printf("tryprop_tag_remap_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,258 +0,0 @@
/*
* 926_match_slice_variant_run — Class B semantic test for task #19.
* Pre-fix wwstage emitted internally-consistent but wrong tag values
* for any `(scalar | []T)` widen/dispatch: every []T slot collapsed
* onto tag 0, so the match arm that fired was always the leading
* variant regardless of which arm the caller meant. cstage was
* correct on these shapes.
*
* 722 pins asm byte-id (the polarity sentinel). This file pins the
* end-to-end runtime: build through each driver and confirm both
* arms reachable, with payload survival on the slice arm.
*
* Matrix per rob (task #19): (u8 | []u8), reverse-order ([]u8 | u8),
* other scalar-vs-slice-of-same-primitive at i8/i32/u64/rune widths,
* and a three-arm (u8 | []u8 | str).
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row { const char *label; const char *src; int want; };
static const struct row rows[] = {
/* The canonical bytes.index shape: scalar arm returns 1, slice
* arm returns 2, and the slice arm also checks payload survives
* (sub.len + first byte) so a silent payload-drop isn't masked. */
{ "u8_slice_u8",
"fn pick(n: (u8 | []u8)) i32 = {\n"
" match (n) {\n"
" case let c: u8 => return c: i32;\n"
" case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [3]u8;\n"
" buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;\n"
" if (pick(42u8) != 42) { return 11; };\n"
" if (pick(buf[0:3]) != 110) { return 12; };\n"
" return 0;\n"
"};\n",
0 },
/* Reverse-order: same shape with slice arm at idx 0. The
* shape-fallback in taggedvariantindex must yield idx 0 here,
* idx 1 for the prior row — the bug's asymmetry catch. */
{ "slice_u8_then_u8",
"fn pick(n: ([]u8 | u8)) i32 = {\n"
" match (n) {\n"
" case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n"
" case let c: u8 => return c: i32;\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [3]u8;\n"
" buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;\n"
" if (pick(buf[0:3]) != 110) { return 11; };\n"
" if (pick(42u8) != 42) { return 12; };\n"
" return 0;\n"
"};\n",
0 },
/* (i8 | []i8) — other signed-narrow primitive. */
{ "i8_slice_i8",
"fn pick(n: (i8 | []i8)) i32 = {\n"
" match (n) {\n"
" case let c: i8 => return c: i32;\n"
" case let s: []i8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [2]i8;\n"
" buf[0] = 5i8; buf[1] = 6i8;\n"
" if (pick(11i8) != 11) { return 11; };\n"
" if (pick(buf[0:2]) != 107) { return 12; };\n"
" return 0;\n"
"};\n",
0 },
/* (i32 | []i32) — wider scalar payload arm. */
{ "i32_slice_i32",
"fn pick(n: (i32 | []i32)) i32 = {\n"
" match (n) {\n"
" case let c: i32 => return c;\n"
" case let s: []i32 => return 1000i32 + (s.len: i32) + s[0];\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [2]i32;\n"
" buf[0] = 33i32; buf[1] = 44i32;\n"
" if (pick(77i32) != 77) { return 11; };\n"
" if (pick(buf[0:2]) != 1035) { return 12; };\n"
" return 0;\n"
"};\n",
0 },
/* (u64 | []u64) — 8-byte scalar arm; the scalar tag at idx 0
* and the slice payload words must not collide. */
{ "u64_slice_u64",
"fn pick(n: (u64 | []u64)) i32 = {\n"
" match (n) {\n"
" case let c: u64 => return c: i32;\n"
" case let s: []u64 => return 1000i32 + (s.len: i32) + (s[0]: i32);\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [2]u64;\n"
" buf[0] = 41u64; buf[1] = 42u64;\n"
" if (pick(99u64) != 99) { return 11; };\n"
" if (pick(buf[0:2]) != 1043) { return 12; };\n"
" return 0;\n"
"};\n",
0 },
/* (rune | []rune) — Hare's iterator-pair shape (a rune or a
* slice of runes). rune is u32-wide, []rune is 24B. */
{ "rune_slice_rune",
"fn pick(n: (rune | []rune)) i32 = {\n"
" match (n) {\n"
" case let r: rune => return r: i32;\n"
" case let s: []rune => return 1000i32 + (s.len: i32) + (s[0]: i32);\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [2]rune;\n"
" buf[0] = 'a'; buf[1] = 'b';\n"
" if (pick('Z') != 90) { return 11; };\n"
" if (pick(buf[0:2]) != 1099) { return 12; };\n"
" return 0;\n"
"};\n",
0 },
/* Three-arm (u8 | []u8 | str): scalar at idx 0, slice at idx 1,
* str at idx 2. Verifies the slice axis composes with the
* pre-existing str shape pass — no regression on str arm. */
{ "u8_slice_u8_str",
"fn pick(n: (u8 | []u8 | str)) i32 = {\n"
" match (n) {\n"
" case let c: u8 => return c: i32;\n"
" case let s: []u8 => return 200i32 + (s.len: i32) + (s[0]: i32);\n"
" case let t: str => return 300i32 + (t.len: i32);\n"
" };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let buf: [3]u8;\n"
" buf[0] = 1u8; buf[1] = 2u8; buf[2] = 3u8;\n"
// Three-arm 32B-slot widening through chained if-call sites
// trips a separate sequential-push bug in both stages; bind
// each call to a let before checking to keep the row focused
// on variant-tag selection.
" let a: i32 = pick(5u8);\n"
" let b: i32 = pick(buf[0:3]);\n"
" let c: i32 = pick(\"hi\");\n"
" if (a != 5) { return 11; };\n"
" if (b != 204) { return 12; };\n"
" if (c != 302) { return 13; };\n"
" return 0;\n"
"};\n",
0 },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[96], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/msv_run_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/msv_run_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/msv_run_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char wdrv[640];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr,
"match_slice_variant_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"match_slice_variant_run[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
if (fail) {
fprintf(stderr,
"match_slice_variant_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("match_slice_variant_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,288 +0,0 @@
/*
* 927_is_nonident_run — `e is T` on a NON-IDENT scrutinee (#45).
*
* Pre-#45 wwstage cgtypetest resolved ONLY lhs.kind == N_IDENT; every
* other scrutinee shape (xs[i], p.field, call()) fell through with
* scrutoff = 0 + scrutt = nil, emitting `MOVQ (BP), AX; CMPQ $0, AX`
* — the tag read landed on the saved-BP word and the variant index
* clamped to 0. SILENT cs≠ww miscompile (cstage N_TYPETEST cgexprs
* the scrutinee and compares the real tag in AX); the regex fold-2a
* epilogue guard `insts[insts.len-1] is inst_match` is the consumer
* that surfaced it. The `as` twin already had the non-ident
* @asrt_spill fallback (#200); `is` mirrors cstage's no-spill shape
* instead (only the tag word is consumed).
*
* Rows pin every scrutinee shape: indexed (constant and len-1 index),
* dot-field, call-result, the `is []T` slice-variant axis on an
* indexed scrutinee, the nullable `(*T | void)` pointer-vs-null
* discriminator on both ident and non-ident scrutinees (review
* finding: the first non-ident arm tag-compared the pointer; the
* ident half was missing the nullable arm since before #45), and the
* ident control (asm hand-checked unchanged vs the pre-#45 compiler
* when the fix landed). Per row:
* w6c vs w6c_ww byte-id (rule 10 — cstage is the runtime-correct
* reference shape) + runtime via both the ww and ww_ww drivers.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want;
};
#define P48_TYPES \
"type p48 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n" \
"type small = (p48 | bool);\n"
static const struct row rows[] = {
/* The discovering repro: indexed scrutinee, constant and
* xs.len-1 index, asserted both polarities (a bool-built
* element must answer `is bool` true and `is p48` false —
* pre-#45 wwstage answered `is p48` TRUE, exit 2). */
{ "indexed",
P48_TYPES
"export fn main() i32 = {\n"
" let xs: []small;\n"
" let b: bool = true;\n"
" let v: small = b;\n"
" append(xs, v);\n"
" if (xs.len == 0 || !(xs[xs.len - 1] is bool)) {\n"
" return 1;\n"
" };\n"
" if (xs[xs.len - 1] is p48) {\n"
" return 2;\n"
" };\n"
" if (!(xs[0] is bool)) { return 3; };\n"
" return 0;\n"
"};\n",
0 },
/* Dot-field scrutinee. */
{ "dot_field",
P48_TYPES
"type holder = struct { v: small, k: i64 };\n"
"export fn main() i32 = {\n"
" let b: bool = true;\n"
" let h: holder = holder { v = b, k = 5 };\n"
" if (!(h.v is bool)) { return 1; };\n"
" if (h.v is p48) { return 2; };\n"
" return 0;\n"
"};\n",
0 },
/* Call-result scrutinee (register-class tagged return; the
* sret-class call stays #38b loud-stopped). */
{ "call_result",
"type val = (i64 | bool);\n"
"fn mk(flag: bool) val = {\n"
" if (flag) {\n"
" let b: bool = true;\n"
" return b;\n"
" };\n"
" let n: i64 = 7;\n"
" return n;\n"
"};\n"
"export fn main() i32 = {\n"
" if (!(mk(true) is bool)) { return 1; };\n"
" if (!(mk(false) is i64)) { return 2; };\n"
" if (mk(true) is i64) { return 3; };\n"
" return 0;\n"
"};\n",
0 },
/* `is []T` on an indexed scrutinee — the flatslicevariantidx
* axis of the non-ident variant resolution. */
{ "slice_variant_indexed",
"type val = (i64 | []u8);\n"
"export fn main() i32 = {\n"
" let xs: []val;\n"
" let buf: [2]u8 = [1u8, 2u8];\n"
" let s: []u8 = buf[0:2];\n"
" let v: val = s;\n"
" append(xs, v);\n"
" let n: i64 = 5;\n"
" let w: val = n;\n"
" append(xs, w);\n"
" if (!(xs[0] is []u8)) { return 1; };\n"
" if (!(xs[1] is i64)) { return 2; };\n"
" if (xs[0] is i64) { return 3; };\n"
" return 0;\n"
"};\n",
0 },
/* Nullable `(*T | void)` fold on a NON-IDENT scrutinee: the
* discriminator is pointer-vs-null, not tag-vs-index (cstage
* N_TYPETEST nullable arm emits CMPQ $0 + JE/JNE polarity).
* Surfaced in #45 review: the first non-ident arm compared the
* pointer against the variant index — `h.m is *t` on a non-null
* pointer answered FALSE (silent cs≠ww, wwstage exit 1). Both
* polarities, both states (ptr and void). */
{ "nullable_dot_field",
"type t = struct { a: i64 };\n"
"type maybe = (*t | void);\n"
"type holder = struct { m: maybe, k: i64 };\n"
"export fn main() i32 = {\n"
" let v: t = t { a = 5 };\n"
" let h: holder = holder { m = &v, k = 1 };\n"
" if (!(h.m is *t)) { return 1; };\n"
" if (h.m is void) { return 2; };\n"
" let h2: holder = holder { m = void, k = 2 };\n"
" if (h2.m is *t) { return 3; };\n"
" if (!(h2.m is void)) { return 4; };\n"
" return 0;\n"
"};\n",
0 },
/* Nullable IDENT scrutinee: the same missing nullable arm hit
* the ident path too (pre-existing at master, closed by the same
* shared compare choke-point). */
{ "nullable_ident",
"type t = struct { a: i64 };\n"
"type maybe = (*t | void);\n"
"export fn main() i32 = {\n"
" let v: t = t { a = 5 };\n"
" let m: maybe = &v;\n"
" if (!(m is *t)) { return 1; };\n"
" if (m is void) { return 2; };\n"
" let m2: maybe = void;\n"
" if (m2 is *t) { return 3; };\n"
" if (!(m2 is void)) { return 4; };\n"
" return 0;\n"
"};\n",
0 },
/* Ident control: the pre-existing slot-load path must be
* untouched (also hand-cmp'd vs the pre-#45 w6c_ww). */
{ "ident_control",
"type val = (i64 | bool);\n"
"export fn main() i32 = {\n"
" let b: bool = true;\n"
" let v: val = b;\n"
" if (!(v is bool)) { return 1; };\n"
" if (v is i64) { return 2; };\n"
" return 0;\n"
"};\n",
0 },
};
static const char *g_bin;
static int
compile_s(const char *tool, const char *src, const char *outpath)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1",
g_bin, tool, src, outpath);
return runwait(cmd);
}
static int
file_eq(const char *a, const char *b)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b);
return runwait(cmd) == 0;
}
static int
run_driver(const char *driver, const char *src, const char *label)
{
char tmpdir[128], outbin[160], rmcmd[200], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/isnonid_%d_d", getpid());
mkdir(tmpdir, 0755);
snprintf(outbin, sizeof outbin, "%s/m", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
return got;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
static char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
g_bin = bin;
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char src[128], cs_s[128], ww_s[128];
snprintf(src, sizeof src, "/tmp/isnonid_%d_%d.ww",
getpid(), i);
snprintf(cs_s, sizeof cs_s, "/tmp/isnonid_%d_%d_cs.s",
getpid(), i);
snprintf(ww_s, sizeof ww_s, "/tmp/isnonid_%d_%d_ww.s",
getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) return 1;
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
total++;
int cs_rc = compile_s("w6c", src, cs_s);
int ww_rc = compile_s("w6c_ww", src, ww_s);
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
unlink(src); unlink(cs_s); unlink(ww_s);
continue;
}
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
fail++;
}
int got_cs = run_driver("ww", src, r->label);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
fail++;
}
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", src, r->label);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
r->label, r->want, got_ww);
fail++;
}
}
unlink(src); unlink(cs_s); unlink(ww_s);
}
if (fail) {
fprintf(stderr, "is_nonident_run: %d/%d rows failed\n",
fail, total);
return 1;
}
printf("is_nonident_run: %d rows ok\n", total);
return 0;
}

View File

@@ -1,290 +0,0 @@
/*
* 928_match_nonident_idx_run — `match` on an N_INDEX scrutinee whose
* BASE is not an ident (#48).
*
* Pre-#48 wwstage matchscrutt's N_INDEX arm required ibase.kind ==
* N_IDENT; an index over a struct field (`match (h.xs[i])` = N_INDEX
* over N_DOT) returned nil → cgmatch dispatched with scrutt = nil:
* every case arm's variant index clamped to 0 (CMPQ $0) and the
* @match_spill slot fell to the 16B default. SILENT cs≠ww miscompile
* (cstage N_MATCH reads the checker-stamped s->type for every
* scrutinee shape, cmd/w6c/cgen.c:7510); regex fold-2a's
* `match (re.insts[i])` is the consumer that surfaced it. The fix
* resolves the element type from the stamped .type_ (the #45/#67
* stamped-carrier pattern), plus the load-half twin: cgindex's
* generic-fallback tagged-element load was the only arm missing the
* `slot > 24` R8 word (cgen.c:9106-9117), so a >24B-slot element via
* a non-ident base under-read the cursor.
*
* Rows pin: the discovering repro shape (field-base slice index, all
* three variants matched both ways), the regex shape (56B-slot
* inst-like union over r.insts[i]; payload reads stay within the 32B
* cursor — words past R8 are #43's deferred residual), a chained-dot
* base (o.in_.xs[i] — depth independence), and the ident
* scrutinee / ident-base array + slice index controls (byte-id at
* master per the #48 scoping probes; cs is untouched by the fix, so
* the per-row cs==ww cmp pins them unchanged). A call-BASE index
* (`mk()[0]`) is NOT covered: cgindex's element classification (esz +
* tagged) skips N_CALL bases — pre-existing sibling, filed separately.
* Per row: w6c vs w6c_ww byte-id (rule 10 — cstage is the
* runtime-correct reference) + runtime via both drivers.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want;
};
#define TU_TYPES \
"type va = void;\n" \
"type vb = bool;\n" \
"type vc = size;\n" \
"type tu = (va | vb | vc);\n"
static const struct row rows[] = {
/* The discovering repro: slice field of a struct, indexed.
* Pre-#48 wwstage dispatched the vb element to the FIRST arm
* regardless of pattern (tag compared against 0) — exit 1. The
* third match pins arm-order independence (variant 1 matched
* from a non-first arm). */
{ "field_slice_idx",
TU_TYPES
"type holder = struct { xs: []tu, n: size };\n"
"export fn main() i32 = {\n"
" let sl: []tu = [(true: vb), ((7: size): vc)];\n"
" let h: holder;\n"
" h.xs = sl;\n"
" h.n = 2;\n"
" match (h.xs[0]) {\n"
" case let b: vb => { if (!(b: bool)) { return 2; }; };\n"
" case => return 1;\n"
" };\n"
" match (h.xs[1]) {\n"
" case let s: vc => { if ((s: size) != 7) { return 4; }; };\n"
" case => return 3;\n"
" };\n"
" match (h.xs[0]) {\n"
" case va => return 5;\n"
" case let b: vb => { if (!(b: bool)) { return 6; }; };\n"
" case vc => return 7;\n"
" };\n"
" return 0;\n"
"};\n",
0 },
/* The regex fold-2a shape: 56B-slot inst-like union (8B tag +
* 48B widest payload) matched over a struct-field slice index.
* The big variant drives slot/spill sizing past 24B (the R8
* word both in cgindex's fallback load and cgmatch's spill);
* matched payloads read word 1 only — within the 32B cursor. */
{ "regex_inst_56b",
"type big = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n"
"type ilit = struct { r: i64 };\n"
"type imatch = void;\n"
"type inst = (big | ilit | imatch);\n"
"type re = struct { insts: []inst, n: i64 };\n"
"export fn main() i32 = {\n"
" let sl: []inst;\n"
" let l: ilit = ilit { r = 65 };\n"
" let v: inst = l;\n"
" append(sl, v);\n"
" let m: inst = void: imatch;\n"
" append(sl, m);\n"
" let r: re;\n"
" r.insts = sl;\n"
" r.n = 2;\n"
" match (r.insts[0]) {\n"
" case let x: ilit => { if (x.r != 65) { return 2; }; };\n"
" case => return 1;\n"
" };\n"
" match (r.insts[1]) {\n"
" case imatch => { };\n"
" case => return 3;\n"
" };\n"
" match (r.insts[0]) {\n"
" case big => return 4;\n"
" case let x: ilit => { if (x.r != 65) { return 5; }; };\n"
" case imatch => return 6;\n"
" };\n"
" return 0;\n"
"};\n",
0 },
/* Dot-DEPTH: the index base is a CHAINED dot (o.in_.xs[i] =
* N_INDEX over N_DOT over N_DOT) — pins that the stamped-carrier
* resolve is depth-independent (any non-ident base shape, not
* just one dot level). */
{ "dot_depth_idx",
TU_TYPES
"type inner = struct { xs: []tu, k: size };\n"
"type outer = struct { in_: inner, n: size };\n"
"export fn main() i32 = {\n"
" let sl: []tu = [(true: vb), ((9: size): vc)];\n"
" let o: outer;\n"
" o.in_.xs = sl;\n"
" o.in_.k = 1;\n"
" o.n = 2;\n"
" match (o.in_.xs[0]) {\n"
" case let b: vb => { if (!(b: bool)) { return 2; }; };\n"
" case => return 1;\n"
" };\n"
" match (o.in_.xs[1]) {\n"
" case va => return 3;\n"
" case let s: vc => { if ((s: size) != 9) { return 4; }; };\n"
" case vb => return 5;\n"
" };\n"
" return 0;\n"
"};\n",
0 },
/* Controls: ident scrutinee + ident-base array and slice index
* — the pre-existing matchscrutt/cgmatch paths the fix must not
* disturb (asm byte-id at master per the scoping probes). */
{ "ident_and_identbase_controls",
TU_TYPES
"export fn main() i32 = {\n"
" let v: tu = (true: vb);\n"
" match (v) {\n"
" case let b: vb => { if (!(b: bool)) { return 2; }; };\n"
" case => return 1;\n"
" };\n"
" let arr: [2]tu = [(true: vb), ((7: size): vc)];\n"
" match (arr[1]) {\n"
" case let s: vc => { if ((s: size) != 7) { return 4; }; };\n"
" case => return 3;\n"
" };\n"
" let sl: []tu = [(true: vb), ((7: size): vc)];\n"
" match (sl[0]) {\n"
" case let b: vb => { if (!(b: bool)) { return 6; }; };\n"
" case => return 5;\n"
" };\n"
" return 0;\n"
"};\n",
0 },
};
static const char *g_bin;
static int
compile_s(const char *tool, const char *src, const char *outpath)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1",
g_bin, tool, src, outpath);
return runwait(cmd);
}
static int
file_eq(const char *a, const char *b)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b);
return runwait(cmd) == 0;
}
static int
run_driver(const char *driver, const char *tmpdir, const char *src,
const char *label)
{
char outbin[256], cmd[1024];
snprintf(outbin, sizeof outbin, "%s/%s_bin", tmpdir, driver);
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
return -1;
}
return runwait(outbin);
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
static char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
g_bin = bin;
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char tmpdir[128], src[160], cs_s[160], ww_s[160], rmcmd[192];
snprintf(tmpdir, sizeof tmpdir, "/tmp/matchnidx_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/matchnidx_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/matchnidx_%d_%d_cs.s",
tmpdir, getpid(), i);
snprintf(ww_s, sizeof ww_s, "%s/matchnidx_%d_%d_ww.s",
tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return 1; }
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
total++;
int cs_rc = compile_s("w6c", src, cs_s);
int ww_rc = compile_s("w6c_ww", src, ww_s);
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
runwait(rmcmd);
continue;
}
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
fail++;
}
int got_cs = run_driver("ww", tmpdir, src, r->label);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
fail++;
}
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", tmpdir, src,
r->label);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
r->label, r->want, got_ww);
fail++;
}
}
runwait(rmcmd);
}
if (fail) {
fprintf(stderr, "match_nonident_idx_run: %d/%d rows failed\n",
fail, total);
return 1;
}
printf("match_nonident_idx_run: %d rows ok\n", total);
return 0;
}

View File

@@ -1,198 +0,0 @@
/*
* 931_variant_typekey_run — Class B semantic sentinel for #66
* (Phase-N step 3, the user-ruled type-keying flip).
*
* Locks in the behavioral capability the corpus does NOT exercise:
* distinguishing two co-variants that share a SHAPE (and, pre-flip,
* a name key) by NOMINAL TYPE identity. cstage's cg_variant_match
* (cmd/w6c/cgen.c:451) is what keeps `(str | linerr)` separable even
* though `type linerr = !str` unwraps to str; #66 brings wwstage's
* flatvariantidx to the same typeeq discipline (per-decl TY_NAMED ptr).
*
* Discriminating shape: a tagged union with the alias variant FIRST,
* widened from a value with no surface type name (a call return).
* Pre-#66 wwstage routed the value→tag direction through rhstargetname
* (surface name) + a str/scalar shape fallback that picked the FIRST
* shape-matching variant — i.e. the leading alias — so a plain `str`
* from a call was tagged as `linerr` (idx 0) instead of `str` (idx 1).
* A subsequent match then fired the wrong arm. cstage always type-keyed,
* so pre-#66 this was a cstage-vs-wwstage divergence; each row exits 10
* on pre-#66 wwstage and 0 on cstage / post-#66 wwstage. The 990-997
* byte-id gates do not cover this because the corpus has no
* same-shape co-variant whose name key and type key disagree.
*
* Pure runtime test: build through both drivers (cstage `ww`, wwstage
* `ww_ww`) and assert each arm fires for its matching input.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src; /* single-module program — written as p.ww */
int want;
};
static const struct row rows[] = {
/* str axis. `(linerr | str)` with linerr = !str, alias first.
* classify(0) widens a str returned by a call (no surface name)
* — pre-#66 the shape fallback tags it as the leading str-shape
* variant linerr (idx 0) and the match returns 1 not 2.
* classify(1) widens a linerr (round-trip soundness). */
{ "linerr_str_alias_first_callret",
"package main;\n"
"type linerr = !str;\n"
"fn getstr() str = { return \"hi\"; };\n"
"fn produce(k: i32) (linerr | str) = {\n"
" if (k == 0) { return getstr(); };\n"
" let e: linerr = \"boom\";\n"
" return e;\n"
"};\n"
"export fn classify(k: i32) i32 = {\n"
" match (produce(k)) {\n"
" case let er: linerr => return 1;\n"
" case let v: str => return 2;\n"
" };\n"
"};\n"
"export fn main() i32 = {\n"
" if (classify(0) != 2) { return 10; };\n"
" if (classify(1) != 1) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
/* scalar axis. `(ec | i32)` with ec = !i32, alias first.
* classify(0) widens an i32 returned by a call — pre-#66 the
* scalar shape fallback tags it as the leading scalar-shape
* variant ec (idx 0). Confirms the flip isn't str-specific. */
{ "errint_int_alias_first_callret",
"package main;\n"
"type ec = !i32;\n"
"fn getint() i32 = { return 7; };\n"
"fn produce(k: i32) (ec | i32) = {\n"
" if (k == 0) { return getint(); };\n"
" let e: ec = 9;\n"
" return e;\n"
"};\n"
"export fn classify(k: i32) i32 = {\n"
" match (produce(k)) {\n"
" case let er: ec => return 1;\n"
" case let v: i32 => return 2;\n"
" };\n"
"};\n"
"export fn main() i32 = {\n"
" if (classify(0) != 2) { return 10; };\n"
" if (classify(1) != 1) { return 11; };\n"
" return 0;\n"
"};\n",
0 },
};
static int
write_file(const char *dir, const char *name, const char *body)
{
char path[512];
snprintf(path, sizeof path, "%s/%s", dir, name);
FILE *f = fopen(path, "wb");
if (!f) return -1;
fputs(body, f);
fclose(f);
return 0;
}
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[96], cmd[2048];
snprintf(tmpdir, sizeof tmpdir, "/tmp/vtk_%d_%d", getpid(), i);
mkdir(tmpdir, 0755);
if (write_file(tmpdir, "p.ww", r->src) != 0) return -1;
snprintf(cmd, sizeof cmd,
"cd %s && %s build p.ww 2>/dev/null", tmpdir, driver);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
runwait(cmd);
return -1;
}
char outbin[160];
snprintf(outbin, sizeof outbin, "%s/p", tmpdir);
int got = runwait(outbin);
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
runwait(cmd);
return got;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[640];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
char wdrv[640];
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *path; int gated_on_existence; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated_on_existence
&& access(drivers[d].path, X_OK) != 0) {
fprintf(stderr,
"variant_typekey_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].path);
continue;
}
for (int i = 0; i < n; i++) {
int got = run_driver(drivers[d].path, &rows[i], i);
total++;
if (got != rows[i].want) {
fprintf(stderr,
"variant_typekey_run[%s][%s]: exit=%d want=%d\n",
drivers[d].name, rows[i].label,
got, rows[i].want);
fail++;
}
}
}
if (fail) {
fprintf(stderr,
"variant_typekey_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("variant_typekey_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,679 +0,0 @@
/*
* 938_tagged_structlit_payload_run — #23 (fold-5b prereq): tagged-union
* CONSTRUCTION from an INLINE struct-literal payload whose fields are
* themselves tagged ((void|size) — the regex inst_repeat shape,
* ref/hare/regex/regex.ha:55-60). Pre-#23 the widen choke-point's
* struct-payload arm (cg_widen_tagged_store / cgwidentaggedstorebp)
* carried its OWN inline N_STRUCTLIT field loop — a parallel fill that
* drifted from cg_structlit_fill / cgstructlitfill: it had float/str/
* slice/scalar arms but NO tagged-field widen arm, so a (void|T)-typed
* field's raw scalar landed in the field's TAG word (silent truncation
* past the first tagged field; both stages, byte-id — gate-blind;
* prober-9 PG5 evidence, re-confirmed at 6699158). The fix deletes the
* parallel loop and delegates to the canonical fill, which also
* inherits the nested-struct field arm.
*
* row | shape | want
* ---------------------+----------------------------------------+-----
* inst_repeat_shape | rep{id,origin,min=void,max=5:size} — |
* | the PG5b repro; 48B payload | 0
* rep3_allsize | {id,min,max} all set via `: size` |
* | casts — PG5g; tag+payload word checks | 0
* tagged_only16 | single (void|size) field — 16B payload | 0
* prebound_tagged_field| min = mn (pre-bound (void|size) LOCAL |
* | — tagged→tagged subset inside the fill)| 0
* str_and_tagged | {s:str, m:(void|size)} — 3-word str |
* | arm + tagged arm in one literal | 0
* variant_pos0 | union (rep|lit) — struct variant tag 0 | 0
* slice_elem_cursorfit | 24B payload through append + indexed |
* | match (union 32B = exactly the 4-word |
* | tagged cursor; >32B indexed reads are |
* | the open over-cap cursor residual, |
* | task #37 — NOT pinned here) | 0
* nested_struct_field | nested struct-typed field literal in a |
* | union payload — delegation-inherited |
* | recursion arm (pre-#23: AX-only store) | 0
* ellipsis_autofill | `...` autofill + tagged field — the |
* | fill's TK_ELLIPSIS zero-fill runs at |
* | the widened slot's payload base | 0
* two_tagged_offset0 | tagged field at struct OFFSET 0, a 2nd |
* | tagged field, then a trailing scalar | 0
* nested_and_tagged | nested struct field AND tagged field |
* | in ONE literal — fill→widen→fill, both |
* | recursion directions in one shape | 0
* voidstr_field | (void|str) tagged field — 3-word str |
* | payload through the fill's widen arm; |
* | pre-#23 the garbage tag matched NO |
* | variant (fallthrough-proof readback) | 0
* recursion_torture | 3-level tagged(struct{tagged(struct{ |
* | tagged(leaf-lit)})}) — the widen↔fill |
* | mutual recursion terminates + correct | 0
* payload8_boundary | struct payload exactly 8B (single size |
* | field) — smallest-payload guard | 0
* scalar_neighbor | scalar+f64+str payload, NO tagged |
* | field — emission no-regress guard | 0
*
* Field readback goes through MATCH BINDINGS only: direct field reads
* on a struct-w/-tagged-field LOCAL are the open #24 (wwstage
* field(SB) fallback). Tagged-field inits use explicit `: size`
* casts in the #23 rows; the BARE untyped-int form (the old #33
* wwstage mis-tag) is pinned by its own row (untyped_int_bare_widen)
* since the cgvariantmatch untyped arm landed. The chained-dot
* tagged-leaf read/assign family (#38a) is pinned by the
* chained_* rows below since the dot-spine tagged arms landed.
*
* Every K_RUN row also asserts cstage/wwstage asm byte-id. NNN<950,
* self-contained (/tmp, no imports) — rule-14's selfhost-sibling race
* does not apply (936/941 precedent).
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa), cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
return rc;
}
/* noasm: skip the cs/ww byte-id check for rows whose READBACK needs an
* inner `match` on a tagged FIELD scrutinee — wwstage routes that load
* through a 4-word cursor scratch where cstage copies field words
* directly (PRE-EXISTING divergence, confirmed at master 6699158 on
* the same sources; construction asm is byte-id up to the readback —
* the regex-arc match-on-tagged-struct-field family, needs a filed
* task). The skip is printed loudly so it can't rot silently. */
struct row { const char *label; const char *src; int want; int noasm; };
static const struct row rows[] = {
/* the PG5b repro verbatim: only id/origin survived pre-#23
* (min's tag word read 0 = void by accident, max's read 5). */
{ "inst_repeat_shape",
"package main;\n"
"type inst_lit = rune;\n"
"type rep = struct {\n"
" id: size,\n"
" origin: size,\n"
" min: (void | size),\n"
" max: (void | size),\n"
"};\n"
"type inst = (inst_lit | rep);\n"
"export fn main() i32 = {\n"
" let v: inst = rep { id = 1: size, origin = 2: size, min = void, max = 5: size };\n"
" match (v) {\n"
" case let ir: rep => {\n"
" if (ir.id != 1) { return 1; };\n"
" if (ir.origin != 2) { return 2; };\n"
" if (!(ir.min is void)) { return 3; };\n"
" if (!(ir.max is size)) { return 4; };\n"
" if (ir.max as size != 5) { return 5; };\n"
" };\n"
" case let l: inst_lit => { return 6; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* PG5g: every tagged field set — pre-#23 the raw 2/5 landed in
* the tag words and both is-tests failed. */
{ "rep3_allsize",
"package main;\n"
"type inst_lit = rune;\n"
"type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n"
"type inst = (inst_lit | rep3);\n"
"export fn main() i32 = {\n"
" let v: inst = rep3 { id = 1: size, min = 2: size, max = 5: size };\n"
" match (v) {\n"
" case let ir: rep3 => {\n"
" if (ir.id != 1) { return 1; };\n"
" if (!(ir.min is size)) { return 2; };\n"
" if (ir.min as size != 2) { return 3; };\n"
" if (!(ir.max is size)) { return 4; };\n"
" if (ir.max as size != 5) { return 5; };\n"
" };\n"
" case let l: inst_lit => { return 6; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* minimal payload: the tagged field IS the whole struct. */
{ "tagged_only16",
"package main;\n"
"type only = struct { m: (void | size) };\n"
"type u = (rune | only);\n"
"export fn main() i32 = {\n"
" let v: u = only { m = 7: size };\n"
" match (v) {\n"
" case let o: only => {\n"
" if (!(o.m is size)) { return 1; };\n"
" if (o.m as size != 7) { return 2; };\n"
" };\n"
" case => return 3;\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* field value is a pre-bound tagged LOCAL — the fill's widen arm
* takes the tagged→tagged identity-copy route. */
{ "prebound_tagged_field",
"package main;\n"
"type inst_lit = rune;\n"
"type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n"
"type inst = (inst_lit | rep3);\n"
"export fn main() i32 = {\n"
" let mn: (void | size) = 2: size;\n"
" let v: inst = rep3 { id = 3: size, min = mn, max = void };\n"
" match (v) {\n"
" case let ir: rep3 => {\n"
" if (ir.id != 3) { return 1; };\n"
" if (!(ir.min is size)) { return 2; };\n"
" if (ir.min as size != 2) { return 3; };\n"
" if (!(ir.max is void)) { return 4; };\n"
" };\n"
" case let l: inst_lit => { return 5; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* str field (3-word arm) and tagged field in the same literal —
* the arms must not disturb each other's offsets. */
{ "str_and_tagged",
"package main;\n"
"type inst_lit = rune;\n"
"type named = struct { s: str, m: (void | size) };\n"
"type inst = (inst_lit | named);\n"
"export fn main() i32 = {\n"
" let v: inst = named { s = \"hi\", m = 9: size };\n"
" match (v) {\n"
" case let nm: named => {\n"
" if (len(nm.s) != 2) { return 1; };\n"
" if (!(nm.m is size)) { return 2; };\n"
" if (nm.m as size != 9) { return 3; };\n"
" };\n"
" case let l: inst_lit => { return 4; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* struct variant FIRST in the union — tag 0; pins that the fix
* is position-independent. */
{ "variant_pos0",
"package main;\n"
"type inst_lit = rune;\n"
"type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n"
"type inst = (rep3 | inst_lit);\n"
"export fn main() i32 = {\n"
" let v: inst = rep3 { id = 1: size, min = void, max = 8: size };\n"
" match (v) {\n"
" case let ir: rep3 => {\n"
" if (ir.id != 1) { return 1; };\n"
" if (!(ir.min is void)) { return 2; };\n"
" if (!(ir.max is size)) { return 3; };\n"
" if (ir.max as size != 8) { return 4; };\n"
" };\n"
" case let l: inst_lit => { return 5; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* construction → append → indexed match, end-to-end. Payload 24B
* keeps the union at 32B — exactly the 4-word tagged cursor, so
* the indexed read is in-cap. The >32B indexed read truncates at
* the cursor (open over-cap residual, #22b/#36 family) — the
* 48B-payload rows above therefore match the LOCAL only. */
{ "slice_elem_cursorfit",
"package main;\n"
"type inst_lit = rune;\n"
"type rep1 = struct { id: size, m: (void | size) };\n"
"type inst = (inst_lit | rep1);\n"
"export fn main() i32 = {\n"
" let insts: []inst = [];\n"
" append(insts, ('a': inst_lit));\n"
" let v: inst = rep1 { id = 4: size, m = 6: size };\n"
" append(insts, v);\n"
" if (len(insts) != 2) { return 1; };\n"
" match (insts[1]) {\n"
" case let r: rep1 => {\n"
" if (r.id != 4) { return 2; };\n"
" if (!(r.m is size)) { return 3; };\n"
" if (r.m as size != 6) { return 4; };\n"
" };\n"
" case let l: inst_lit => { return 5; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* nested struct-typed field literal inside the union payload —
* rides the canonical fill's recursion arm the delegation
* inherits (pre-#23 the parallel loop stored AX only). */
{ "nested_struct_field",
"package main;\n"
"type inner = struct { a: size, b: size };\n"
"type outer = struct { id: size, in_: inner };\n"
"type u = (rune | outer);\n"
"export fn main() i32 = {\n"
" let v: u = outer { id = 1: size, in_ = inner { a = 2: size, b = 3: size } };\n"
" match (v) {\n"
" case let o: outer => {\n"
" if (o.id != 1) { return 1; };\n"
" if (o.in_.a != 2) { return 2; };\n"
" if (o.in_.b != 3) { return 3; };\n"
" };\n"
" case => return 4;\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* `...` autofill + tagged field (ken n7): the fill's TK_ELLIPSIS
* zero-fill loop must run at the widened slot's payload base, not
* the slot base — pre-#23 exit 3 (raw 5 in m's tag word). */
{ "ellipsis_autofill",
"package main;\n"
"type st = struct { a: u64, m: (void | size) };\n"
"export fn main() i32 = {\n"
" let e: (void | st) = st { m = 5: size, ... };\n"
" match (e) {\n"
" case let s: st => {\n"
" if (s.a != 0u64) { return 2; };\n"
" if (!(s.m is size)) { return 3; };\n"
" if (s.m as size != 5) { return 4; };\n"
" };\n"
" case void => { return 9; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* tagged field at struct OFFSET 0 plus a second tagged field and
* a TRAILING scalar (ken n2b) — pins offset-0 tag placement and
* the scalar offset computed past two 16B tagged fields. */
{ "two_tagged_offset0",
"package main;\n"
"type d2 = struct { m: (void | size), n: (void | i64), z: u64 };\n"
"export fn main() i32 = {\n"
" let e: (void | d2) = d2 { m = 4: size, n = (-9): i64, z = 77u64 };\n"
" match (e) {\n"
" case let s: d2 => {\n"
" if (!(s.m is size)) { return 2; };\n"
" if (s.m as size != 4) { return 3; };\n"
" if (!(s.n is i64)) { return 4; };\n"
" if (s.n as i64 != -9) { return 5; };\n"
" if (s.z != 77u64) { return 6; };\n"
" };\n"
" case void => { return 9; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* nested struct field AND tagged field in the SAME literal (ken
* n5b) — exercises fill→fill (nested) and fill→widen (tagged)
* from one delegated call. */
{ "nested_and_tagged",
"package main;\n"
"type inn = struct { v: u64 };\n"
"type outt = struct { i: inn, m: (void | size) };\n"
"export fn main() i32 = {\n"
" let e: (void | outt) = outt { i = inn { v = 11u64 }, m = 8: size };\n"
" match (e) {\n"
" case let s: outt => {\n"
" if (s.i.v != 11u64) { return 2; };\n"
" if (!(s.m is size)) { return 3; };\n"
" if (s.m as size != 8) { return 4; };\n"
" };\n"
" case void => { return 9; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* (void|str) tagged field — 3-word str payload through the fill's
* widen arm. Readback initialises `got = 9` BEFORE the inner
* match so a garbage tag matching NO variant can't fall through
* to a fake 0 (pre-#23 the raw str ptr landed in the tag word
* and exactly that fallthrough faked a pass). */
{ "voidstr_field",
"package main;\n"
"type st = struct { id: size, s: (void | str) };\n"
"type u = (rune | st);\n"
"export fn main() i32 = {\n"
" let v: u = st { id = 3: size, s = \"hello\" };\n"
" match (v) {\n"
" case let o: st => {\n"
" if (o.id != 3) { return 1; };\n"
" let got: i32 = 9;\n"
" match (o.s) {\n"
" case let sv: str => {\n"
" if (len(sv) == 5) { got = 0; } else { got = 2; };\n"
" };\n"
" case void => { got = 3; };\n"
" };\n"
" return got;\n"
" };\n"
" case => return 4;\n"
" };\n"
" return 5;\n"
"};\n", 0, 1 },
/* 3-level widen↔fill mutual recursion: tagged(struct{tagged(
* struct{tagged(struct-lit)})}) — termination AND correctness of
* the #23 delegation cycle (pre-#23 exit 4: mid's tag dropped). */
{ "recursion_torture",
"package main;\n"
"type leaf = struct { p: size };\n"
"type t1 = (void | leaf);\n"
"type mid = struct { t: t1 };\n"
"type t2 = (void | mid);\n"
"type outr = struct { id: size, t: t2 };\n"
"type u = (rune | outr);\n"
"export fn main() i32 = {\n"
" let v: u = outr { id = 1: size, t = mid { t = leaf { p = 9: size } } };\n"
" match (v) {\n"
" case let o: outr => {\n"
" if (o.id != 1) { return 1; };\n"
" match (o.t) {\n"
" case let m: mid => {\n"
" match (m.t) {\n"
" case let l: leaf => {\n"
" if (l.p != 9) { return 2; };\n"
" return 0;\n"
" };\n"
" case void => { return 3; };\n"
" };\n"
" };\n"
" case void => { return 4; };\n"
" };\n"
" };\n"
" case => return 5;\n"
" };\n"
" return 6;\n"
"};\n", 0, 1 },
/* struct payload exactly 8B — the smallest payload through the
* widen→fill delegation (boundary guard; worked pre-#23 too). */
{ "payload8_boundary",
"package main;\n"
"type one = struct { a: size };\n"
"type u = (rune | one);\n"
"export fn main() i32 = {\n"
" let v: u = one { a = 42: size };\n"
" match (v) {\n"
" case let o: one => { if (o.a != 42) { return 1; }; };\n"
" case => return 2;\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* no tagged field at all — the previously-working scalar/float/
* str arms must emit unchanged through the delegation. */
{ "scalar_neighbor",
"package main;\n"
"type plain = struct { a: size, f: f64, s: str };\n"
"type u = (rune | plain);\n"
"export fn main() i32 = {\n"
" let v: u = plain { a = 11: size, f = 2.5, s = \"xyz\" };\n"
" match (v) {\n"
" case let p: plain => {\n"
" if (p.a != 11) { return 1; };\n"
" if (p.f != 2.5) { return 2; };\n"
" if (len(p.s) != 3) { return 3; };\n"
" };\n"
" case => return 4;\n"
" };\n"
" return 0;\n"
"};\n", 0, 0 },
/* #33: a BARE untyped-int init into (void|T) — pre-fix wwstage's
* variant scan fell to the str/slice shape fallback whose first
* non-str/slice variant is `void` (tag 0 stored, is-test expects
* 1; cstage resolved via type_assignable). Pins let-init, assign
* after void, arg widen, the bool-leading skip, and the signed
* (void|i64) variant (ken hB3); the cast form rides alongside as
* the no-drift control (it was always right). */
/* #38a: chained-dot TAGGED leaf — the dot-spine walkers (value
* chain + ptr chain, both stages) had no TY_TAGGED leaf arm, so
* READS pulled one word (tag; as/match consumers read stale DX
* payload, ken x5c want-28-got-20) and ASSIGNS stored the rhs
* over the TAG slot (ken x5d). Six rows: literal-init read,
* assign-then-read, depth-3 chain read-before-write (ken hB1),
* (void|str) 32B box incl. void-over-str write (ken hB2),
* *outer root, mid-chain *rep. The INDEX-spine sibling
* (xs[i].min) is filed separately (task #58) — NOT a row here. */
{ "chained_tagged_literal_read",
"package main;\n"
"type rep = struct { id: size, min: (void | size), name: (void | str) };\n"
"type outer = struct { tag: size, r: rep };\n"
"export fn main() i32 = {\n"
" let o = outer { tag = 4: size, r = rep { id = 6: size, min = 8: size, name = void } };\n"
" if (o.tag != 4) { return 1; };\n"
" if (o.r.id != 6) { return 2; };\n"
" if (!(o.r.min is size)) { return 3; };\n"
" if (o.r.min as size != 8) { return 4; };\n"
" if (!(o.r.name is void)) { return 5; };\n"
" return 0;\n"
"};\n", 0, 0 },
{ "chained_tagged_assign",
"package main;\n"
"type rep = struct { id: size, min: (void | size) };\n"
"type outer = struct { tag: size, r: rep };\n"
"export fn main() i32 = {\n"
" let o: outer = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n"
" o.r.min = 8: size;\n"
" if (!(o.r.min is size)) { return 1; };\n"
" if (o.r.min as size != 8) { return 2; };\n"
" o.r.min = 9;\n"
" if (!(o.r.min is size)) { return 3; };\n"
" if (o.r.min as size != 9) { return 4; };\n"
" return 0;\n"
"};\n", 0, 0 },
{ "chained_tagged_depth2",
"package main;\n"
"type rep = struct { id: size, min: (void | size) };\n"
"type mid = struct { m: size, r: rep };\n"
"type outer = struct { tag: size, w: mid };\n"
"export fn main() i32 = {\n"
" let o = outer { tag = 4: size, w = mid { m = 2: size, r = rep { id = 6: size, min = 5: size } } };\n"
" if (!(o.w.r.min is size)) { return 5; };\n"
" if (o.w.r.min as size != 5) { return 6; };\n"
" o.w.r.min = 8: size;\n"
" if (o.w.m != 2) { return 1; };\n"
" if (o.w.r.id != 6) { return 2; };\n"
" if (!(o.w.r.min is size)) { return 3; };\n"
" if (o.w.r.min as size != 8) { return 4; };\n"
" return 0;\n"
"};\n", 0, 0 },
{ "chained_tagged_strvariant",
"package main;\n"
"type rep = struct { id: size, nm: (void | str) };\n"
"type outer = struct { tag: size, r: rep };\n"
"export fn main() i32 = {\n"
" let o = outer { tag = 4: size, r = rep { id = 6: size, nm = void } };\n"
" o.r.nm = \"hello\";\n"
" if (!(o.r.nm is str)) { return 1; };\n"
" if ((o.r.nm as str).len != 5) { return 2; };\n"
" match (o.r.nm) {\n"
" case let s: str => { if (s.len != 5) { return 3; }; };\n"
" case void => { return 4; };\n"
" };\n"
" o.r.nm = void;\n"
" if (!(o.r.nm is void)) { return 5; };\n"
" if (o.r.id != 6) { return 6; };\n"
" return 0;\n"
"};\n", 0, 0 },
{ "chained_tagged_ptrroot",
"package main;\n"
"type rep = struct { id: size, min: (void | size) };\n"
"type outer = struct { tag: size, r: rep };\n"
"export fn main() i32 = {\n"
" let o = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n"
" let p: *outer = &o;\n"
" p.r.min = 8: size;\n"
" if (p.r.id != 6) { return 1; };\n"
" if (!(p.r.min is size)) { return 2; };\n"
" if (p.r.min as size != 8) { return 3; };\n"
" return 0;\n"
"};\n", 0, 0 },
{ "chained_tagged_ptrmid",
"package main;\n"
"type rep = struct { id: size, min: (void | size) };\n"
"type outer = struct { tag: size, p: *rep };\n"
"export fn main() i32 = {\n"
" let r = rep { id = 6: size, min = void };\n"
" let o = outer { tag = 4: size, p = &r };\n"
" o.p.min = 8: size;\n"
" if (o.p.id != 6) { return 1; };\n"
" if (!(o.p.min is size)) { return 2; };\n"
" if (o.p.min as size != 8) { return 3; };\n"
" return 0;\n"
"};\n", 0, 0 },
{ "untyped_int_bare_widen",
"package main;\n"
"fn take(v: (void | size)) i32 = {\n"
" if (v is size) { return (v as size): i32; };\n"
" return -1;\n"
"};\n"
"export fn main() i32 = {\n"
" let e: (void | size) = 5;\n"
" if (!(e is size)) { return 1; };\n"
" if (e as size != 5) { return 2; };\n"
" let mn: (void | size) = void;\n"
" mn = 7;\n"
" if (!(mn is size)) { return 3; };\n"
" if (mn as size != 7) { return 4; };\n"
" let b: (bool | u64) = 9;\n"
" if (!(b is u64)) { return 5; };\n"
" if (take(9) != 9) { return 6; };\n"
" let cf: (void | size) = 5: size;\n"
" if (!(cf is size)) { return 7; };\n"
" if (cf as size != 5) { return 8; };\n"
" let f: (void | i64) = 9;\n"
" if (!(f is i64)) { return 9; };\n"
" if (f as i64 != 9) { return 10; };\n"
" return 0;\n"
"};\n", 0, 0 },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tsp_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/tsp_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/tsp_%d_%d", tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>%s",
driver, outbin, src, errf);
int brc = runwait(cmd);
if (brc != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
if (got != r->want) {
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
r->label, driver, got, r->want);
return 1;
}
return 0;
}
/* cs==ww .s byte-id (rule 10). */
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[96], cs[96], ws[96], cmd[1024];
snprintf(src, sizeof src, "/tmp/tsp_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/tsp_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/tsp_asm_%d_%d_w.s", getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
}
int rc = slurp_eq(cs, ws);
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
return rc;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[2080];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[2120], wdrv[2120];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
total++;
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
}
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {
total++;
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
}
for (int i = 0; i < n; i++) {
if (rows[i].noasm) {
printf("row[%s]: asm byte-id SKIPPED "
"(pre-existing match-on-tagged-field "
"cs/ww readback divergence, "
"master-confirmed)\n", rows[i].label);
continue;
}
total++;
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
}
}
if (fail) {
fprintf(stderr, "tagged_structlit_payload: %d/%d checks failed\n",
fail, total);
return 1;
}
printf("tagged_structlit_payload: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,369 +0,0 @@
/*
* 944_idx_tagged_field_run — #58 INDEXED-element TAGGED-field read+assign.
*
* `xs[i].f` where f is a TAGGED union field of a struct array element was
* broken on BOTH stages, byte-IDENTICAL and SILENT (#263 gate-blind; the
* byte-id gate cannot see it, runtime is the only net). The `arr[i].field`
* spine had arms for array / str/slice / float but NO TY_TAGGED arm, so a
* tagged field fell to the single-word scalar fallback:
* READ: loaded only the tag word; the payload cursor (DX/CX/R8) was
* never loaded -> `xs[i].f as T` read stale DX (#38a residual).
* ASSIGN: DOUBLY broken -- stored the raw unboxed scalar into the TAG
* slot, corrupting the box (never boxed, never wrote payload).
* The fix inserts a TY_TAGGED arm before each scalar fallback, both
* stages (align-BOTH, one commit). READ: AX=&xs[i] -> cursor (AX=tag,
* DX/CX/R8=payload), tag LAST. ASSIGN: box the concrete scalar variant
* (tag-index + payload), store over the indexed dest spine.
*
* row | shape | want
* -------------+------------------------------------------------+-----
* const_read | xs[0].m as size ((void|size), tag1) | 0
* var_read | xs[i].m as size variable index | 0
* tag2_read | xs[k].m as size ((void|bool|size), TAG2) | 0
* assign_back | xs[0].m = 8:size; readback via *row ptr | 0
*
* Pre-fix (base cc896bd): const_read/var_read/tag2_read read stale DX
* (payload dropped); assign_back corrupts the box (scalar-into-tag) so
* the ptr-readback control sees the un-overwritten constructed payload
* (99, not 8) -- all four FAIL both stages, cs==ww byte-id (the teeth).
* MULTIPLE tag positions (tag1-of-2 + tag2-of-3) are mandatory: an
* aligned-tag-only pin false-greens the assign scalar-into-tag-slot
* corruption. Post-fix: all four 0/0, cs/ww still byte-identical.
*
* The >32B / multi-word / float-payload arms LOUD-STOP (their real emission
* is not yet wired — #114). These shapes are NOT unconstructible: a wide-box
* union is built via a NARROW variant, so each tripwire is REACHABLE and is
* pinned expect-loud by the cfrows[] rows below. #114 wires the real emission
* and graduates those rows to runtime-value pins.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa), cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
return rc;
}
struct row { const char *label; const char *src; int want; };
static const struct row rows[] = {
{ "const_read",
"package main;\n"
"type opt = (void | size);\n"
"type row = struct { id: int, m: opt };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 7: size },\n"
" row { id = 2, m = 8: size },\n"
" ];\n"
" let v: size = xs[0].m as size;\n"
" if (v != 7) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "var_read",
"package main;\n"
"type opt = (void | size);\n"
"type row = struct { id: int, m: opt };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 7: size },\n"
" row { id = 2, m = 8: size },\n"
" ];\n"
" let i: size = 1;\n"
" let v: size = xs[i].m as size;\n"
" if (v != 8) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "tag2_read",
"package main;\n"
"type opt3 = (void | bool | size);\n"
"type row = struct { id: int, m: opt3 };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 5: size },\n"
" row { id = 2, m = 9: size },\n"
" ];\n"
" let i: size = 1;\n"
" let v: size = xs[i].m as size;\n"
" if (v != 9) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "assign_back",
"package main;\n"
"type opt = (void | size);\n"
"type row = struct { id: int, m: opt };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 99: size },\n"
" row { id = 2, m = 2: size },\n"
" ];\n"
" xs[0].m = 8: size;\n"
" let p: *row = &xs[0];\n"
" let v: size = p.m as size;\n"
" if (v != 8) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
};
/*
* #58 LOUD-STOP TRIPWIRES — expect-compile-fail rows.
*
* The >32B / multi-word / float-payload arms of the indexed tagged-field
* read+store are NOT silently miscompiled: they LOUD-STOP (rule 7). Earlier
* triage believed these shapes were "unconstructible at HEAD" — they are NOT.
* A wide-box union (largest variant >32B) is constructible via a NARROW/scalar
* variant: `(void | size | [5]size)` initialised `m = 7: size` builds and runs
* fine; only reading/assigning that field via the indexed spine reaches the
* tripwire. So each tripwire is REACHABLE and must be pinned as expect-loud:
* a build that SUCCEEDS here would mean the loud-stop regressed into a silent
* miscompile. Each row must (a) fail the build and (b) emit a "#58" tripwire on
* stderr, attributing the CFAIL to this fold (not the #54/#23 construction
* hole, which only fires on STRUCT-LITERAL payloads). When #114 wires real
* >32B box+memcpy emission, it replaces the tripwires AND graduates these rows
* to runtime-value pins.
*/
struct cfrow { const char *label; const char *src; };
static const struct cfrow cfrows[] = {
{ "big_read", /* >32B box read tripwire */
"package main;\n"
"type big = (void | size | [5]size);\n"
"type row = struct { id: int, m: big };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 7: size },\n"
" row { id = 2, m = 8: size },\n"
" ];\n"
" let v: size = xs[0].m as size;\n"
" if (v != 7) { return 1; };\n"
" return 0;\n"
"};\n" },
{ "big_assign", /* >32B box store tripwire */
"package main;\n"
"type big = (void | size | [5]size);\n"
"type row = struct { id: int, m: big };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 7: size },\n"
" row { id = 2, m = 8: size },\n"
" ];\n"
" xs[0].m = 9: size;\n"
" return 0;\n"
"};\n" },
{ "midword_assign", /* multi-word (>16B, <=32B box) store tripwire */
"package main;\n"
"type mid = (void | size | [3]size);\n"
"type row = struct { id: int, m: mid };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 7: size },\n"
" row { id = 2, m = 8: size },\n"
" ];\n"
" xs[0].m = 9: size;\n"
" return 0;\n"
"};\n" },
{ "float_assign", /* float-payload store tripwire */
"package main;\n"
"type fo = (void | f64);\n"
"type row = struct { id: int, m: fo };\n"
"export fn main() i32 = {\n"
" let xs: [2]row = [\n"
" row { id = 1, m = 1.0: f64 },\n"
" row { id = 2, m = 2.0: f64 },\n"
" ];\n"
" xs[0].m = 3.0: f64;\n"
" return 0;\n"
"};\n" },
};
static int
file_has(const char *path, const char *needle)
{
FILE *f = fopen(path, "rb");
if (!f) return 0;
char buf[8192];
size_t n = fread(buf, 1, sizeof buf - 1, f);
fclose(f);
buf[n] = '\0';
return strstr(buf, needle) != NULL;
}
static int
run_cfail(const char *driver, const struct cfrow *r, int i)
{
char tmpdir[96], src[160], outbin[160], errf[160], rmcmd[176], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/itf_cf_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/itf_cf_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/itf_cf_%d_%d", tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd,
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
driver, outbin, src, errf);
int brc = runwait(cmd);
int ok = (brc != 0) && file_has(errf, "#58");
if (!ok)
fprintf(stderr,
"row[%s]: %s build rc=%d, want CFAIL with \"#58\" tripwire\n",
r->label, driver, brc);
runwait(rmcmd);
return ok ? 0 : 1;
}
static int
run_driver(const char *driver, const struct row *r, int i)
{
char tmpdir[96], src[160], outbin[160], errf[160], rmcmd[176], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/itf_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/itf_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/itf_%d_%d", tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd,
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
driver, outbin, src, errf);
int brc = runwait(cmd);
if (brc != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
if (got != r->want) {
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
r->label, driver, got, r->want);
return 1;
}
return 0;
}
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[96], cs[96], ws[96], cmd[1024];
snprintf(src, sizeof src, "/tmp/itf_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/itf_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/itf_asm_%d_%d_w.s", getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
}
int rc = slurp_eq(cs, ws);
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
return rc;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[2080];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[2120], wdrv[2120];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int cn = (int)(sizeof cfrows / sizeof cfrows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
total++;
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
}
/* cstage loud-stop tripwires (cgen.c) — expect CFAIL with "#58". */
for (int i = 0; i < cn; i++) {
total++;
if (run_cfail(cdrv, &cfrows[i], i) != 0) fail++;
}
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {
total++;
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
}
for (int i = 0; i < n; i++) {
total++;
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
}
/* wwstage loud-stop tripwires (cgenexpr.ww). */
for (int i = 0; i < cn; i++) {
total++;
if (run_cfail(wdrv, &cfrows[i], cn + i) != 0) fail++;
}
}
if (fail) {
fprintf(stderr, "idx_tagged_field: %d/%d checks failed\n",
fail, total);
return 1;
}
printf("idx_tagged_field: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,282 +0,0 @@
/*
* 944_tagged_widen_arg_run — #55 tagged-SOURCE arg widened into a wider
* tagged-union PARAM slot (slot-DIFFER).
*
* A call arg whose own type is a NARROWER tagged union (un16=16B) passed
* where the param is a WIDER tagged union (un3=24B). wwstage pushargsrev
* mis-pushed every leg but the lucky aligned-ident: the slot-DIFFER arg
* fell past the slot-gated aistagged arms to the concrete-variant scalar
* boxing (taggedvariantindex<0 → tag clamped to 0 → word0 boxed, real tag
* + high words dropped, NO variant remap). cstage routes EVERY tagged
* source through cg_widen_tagged_store (cgen.c:2982 src_is_tagged) →
* scratch + tag-remap, correct on all 8.
*
* The fix (ww align-UP, 3 coordinated arms in cgenutil.ww):
* 1. the IDENT aistagged gate is now slot-gated (was ungated TRUE) so a
* slot-DIFFER ident falls to the widen scratch instead of the no-
* remap natural push (the prefix-luck);
* 2. pushargsrev's widensz>0 arm routes a tagged source (argistagged)
* through @tagscr + cgwidentaggedstore + push high→low (the
* pname/argistuple twin);
* 3. cgwidentaggedstore's cursor arm (INDEX/DOT ≤32B) spills by the
* SOURCE width, zero-pads, and tag-REMAPS (mirrors cstage's
* cg_widen_tagged_store cursor + shared pad/remap tail, cgen.c:2695-
* 2714) — pre-#55 it spilled by the dst slot (stale high regs) and
* skipped the remap.
*
* row | leg | tag | want
* ----------------+-------+-----------+-----
* ident_aligned | e | i64=tag0 | 42
* deref_aligned | *p | i64=tag0 | 42
* index_aligned | xs[0] | i64=tag0 | 42
* dot_aligned | w.f | i64=tag0 | 42
* ident_remap | e | i64=tag1 | 42 (un16b reorders i64→tag1)
* deref_remap | *p | i64=tag1 | 42
* index_remap | xs[0] | i64=tag1 | 42
* dot_remap | w.f | i64=tag1 | 42
*
* The remap rows carry the MISALIGNED tag: un16b=(bool|i64) puts i64 at
* tag1, widened into un3b=(i64|bool|s16) where i64 is tag0 — so the source
* tag MUST be rewritten or the callee reads the wrong arm. An aligned-only
* suite false-greens a remap-less fix (the exact trap here, ken #55).
*
* Pre-fix: deref/index/dot legs cs42/ww!=42 (silent wrong); aligned-ident
* ww42 by LUCK but asm cs!=ww. Post-fix: all 8 cs42/ww42, all 8 byte-id.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa), cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
return rc;
}
struct row { const char *label; const char *src; int want; };
#define TAKE_ALIGNED \
"type s16 = struct { a: i64, b: i64 };\n" \
"type un16 = (i64 | bool);\n" \
"type un3 = (i64 | bool | s16);\n" \
"fn take(e: un3) i64 = {\n" \
" match (e) {\n" \
" case let v: i64 => { return v; };\n" \
" case let b: bool => { return -1; };\n" \
" case let s: s16 => { return s.a + s.b; };\n" \
" };\n" \
"};\n"
#define TAKE_REMAP \
"type s16 = struct { a: i64, b: i64 };\n" \
"type un16b = (bool | i64);\n" \
"type un3b = (i64 | bool | s16);\n" \
"fn take(e: un3b) i64 = {\n" \
" match (e) {\n" \
" case let v: i64 => { return v; };\n" \
" case let b: bool => { return -1; };\n" \
" case let s: s16 => { return s.a + s.b; };\n" \
" };\n" \
"};\n"
static const struct row rows[] = {
{ "ident_aligned",
"package main;\n" TAKE_ALIGNED
"export fn main() i32 = {\n"
" let v: un16 = (42: i64);\n"
" if (take(v) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "deref_aligned",
"package main;\n" TAKE_ALIGNED
"export fn main() i32 = {\n"
" let v: un16 = (42: i64);\n"
" let p: *un16 = &v;\n"
" if (take(*p) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "index_aligned",
"package main;\n" TAKE_ALIGNED
"export fn main() i32 = {\n"
" let xs: [2]un16 = [(42: i64), (7: i64)];\n"
" if (take(xs[0]) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "dot_aligned",
"package main;\n" TAKE_ALIGNED
"type wrap = struct { f: un16 };\n"
"export fn main() i32 = {\n"
" let w: wrap = wrap { f = (42: i64) };\n"
" if (take(w.f) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
/* misaligned-tag rows — i64 is tag1 in un16b, tag0 in un3b: the
* remap MUST fire or the callee reads the wrong match arm. */
{ "ident_remap",
"package main;\n" TAKE_REMAP
"export fn main() i32 = {\n"
" let v: un16b = (42: i64);\n"
" if (take(v) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "deref_remap",
"package main;\n" TAKE_REMAP
"export fn main() i32 = {\n"
" let v: un16b = (42: i64);\n"
" let p: *un16b = &v;\n"
" if (take(*p) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "index_remap",
"package main;\n" TAKE_REMAP
"export fn main() i32 = {\n"
" let xs: [2]un16b = [(42: i64), (7: i64)];\n"
" if (take(xs[0]) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "dot_remap",
"package main;\n" TAKE_REMAP
"type wrap = struct { f: un16b };\n"
"export fn main() i32 = {\n"
" let w: wrap = wrap { f = (42: i64) };\n"
" if (take(w.f) != 42) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
};
static int
run_driver(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[96], errf[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/twa_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/twa_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/twa_%d_%d", tmpdir, getpid(), i);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd,
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
driver, outbin, src, errf);
int brc = runwait(cmd);
if (brc != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
if (got != r->want) {
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
r->label, driver, got, r->want);
return 1;
}
return 0;
}
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[96], cs[96], ws[96], cmd[1024];
snprintf(src, sizeof src, "/tmp/twa_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/twa_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/twa_asm_%d_%d_w.s", getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
}
int rc = slurp_eq(cs, ws);
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
return rc;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[2080];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[2120], wdrv[2120];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
total++;
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
}
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {
total++;
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
}
for (int i = 0; i < n; i++) {
total++;
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
}
}
if (fail) {
fprintf(stderr, "tagged_widen_arg: %d/%d checks failed\n",
fail, total);
return 1;
}
printf("tagged_widen_arg: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,74 +1,23 @@
/*
* 944_variant_chain_b95_run — #95: variant-match chain-membership arm
* (pass 1b) + structural fallback (c2), BOTH stages fused (cstage
* cg_tag_for_variant + wwstage flatvariantidxt are the same route).
* Pre-#95 a NAMED struct source that was not pointer-identical to a
* NAMED variant fell through every pass and the widen stored tag 0 —
* both stages, byte-identical, gate-blind (the worst class on the
* board: silent wrong tag on VALID code).
* 944_variant_chain_b95_run -- SLIMMED to the one irreducible asymmetric row.
*
* Row sources are ken's #95 first-position oracle
* (.ai/ken-95-oracle.md, probes /tmp/b95/src + kb4/kb5) and rob's
* fold spec (.ai/rob-95-spec.md). Reference: harec
* tagged_select_subtype P1/P2/P3 (ref/harec/src/types.c:702-739,
* :988-1140).
* The #95 variant-match chain-membership corpus (22 rows) migrated to fold-2
* homes (#5-C5): the K_RUN value rows -> test/lang/variant_chain_b95_test.ww
* (@test + T2 byte-id); the two #81-byte-id-waived CALL-source rows ->
* test/lang/variant_chain_b95_runonly_test.ww (value-only); the K_BUILDERR
* reject rows -> test/wcc/data/vchain_<name>/case.ww runww //ww:error carriers.
*
* row | shape (oracle row) | cs/ww
* --------------------+-------------------------------------+------
* chain_1lvl_i | kb5_v2s1i HEADLINE: depth-1 ident |
* | src base → (void|al0) — was both- |
* | wrong-IDENTICAL byte-id tag 0 | 0/0
* chain_2lvl_i | kb95_2lvl_i: ident src depth-2 — |
* | the class extends to any depth | 0/0
* chain_deep_src | kb95_deep_src: src ali2 → (void| |
* | ali) — chain-direction DOWN | 0/0
* chain_deep_var | kb95_deep_var: src base → (void| |
* | ali2) — chain-direction UP | 0/0
* chain_call_bound81 | kb5_v2s1: CALL src 1-lvl — runs |
* | 0/0; byte-id waived: pre-existing |
* | #81 uninit-local zero-fill asm |
* | noise (NO at the #95 base too) | 0/0
* chain_call2_bound81 | kb4_v2_struct2 (#95's original): |
* | CALL src 2-lvl — same #81 waiver | 0/0
* chain_amb_loud | kb95_amb: (ali|ali2) over one base, |
* | src base — TODAY silently tagged |
* | member 0; ≥2 chain hits cannot be |
* | disambiguated in the nominal-lossy |
* | model (harec nassign≥2 → NULL) — |
* | hard-error, twin texts share the |
* | experr tail | err/err
* chain_amb_srcA/B | src ali / src ali2 into (ali|ali2): |
* | pass-1 EXACT fires first (harec P1 |
* | short-circuit) — precedence pins, |
* | MUST NOT MOVE | 0/0
* nom_str / nom_err | (str|linerr) #218 nominal pins: str |
* | src tags str, linerr src tags |
* | linerr — MUST NOT MOVE through the |
* | new arm | 0/0
* exact_ctl | kb5_v2sE2: exact-match ident |
* | control — MUST NOT MOVE | 0/0
* bare_ctl/bare_2lvl | kb4_v2_ctrl / kb4_v2_alias2: pass-2 |
* | bare-source controls — MUST NOT |
* | MOVE | 0/0
* bare_ambig/2 | kb4_v2_ambig(2): pass-2 ambiguity |
* | guards hold their loud texts | err/err
* callret_bound277 | kb5_v2sE: CALL src typed-as-alias — |
* | ww #277 aggregate-return capability |
* | leg, OUT of #95; cells pinned as |
* | observed (cs runs, ww loud) | 0/err
* unrel_struct | kb95_unrel (c2): ta/tb nominally- |
* | unrelated same-layout structs, src |
* | ta → (void|tb) — was both-wrong- |
* | identical byte-id; harec interning |
* | makes dealias(ta)==dealias(tb) → |
* | accept, tag tb (types.c:72-81 + |
* | :1000-1002) | 0/0
* callret_bound277 (kb5_v2sE) CANNOT move: it is cstage-runs / wwstage-rejects
* -- cstage builds+runs it (exit 0) while wwstage LOUD-STOPS with "deferred
* #277" (the ww aggregate-return capability gap, OUT of #95). That asymmetry is
* inexpressible as a single-compile @test (which builds one way) or a runww
* //ww:error carrier (which demands BOTH stages reject). It stays a slim C pin.
*
* K_RUN rows build+run BOTH drivers (exit==want) and assert cstage/
* wwstage asm byte-id. K_RUN_NOID waives byte-id — cite the blocking
* task at the row. K_RUN_CS_WWERR: cstage build+run exit==cswant AND
* wwstage build FAILS with experr (944_alias_accept_run kind). NNN<950,
* self-contained /tmp sources, no imports (944 precedent).
* Non-vacuity (the pin CAN go RED): if cstage stops running it (exit != 0) or
* wwstage starts accepting it (#277 wired), the row fails; the wwstage leg also
* requires the EXACT "deferred #277" diagnostic so an unrelated wwstage error
* cannot masquerade as coverage. Mutation-checked: dropping the cast-to-alias
* (`mk()` typed plainly) flips cstage and was observed RED.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -86,36 +35,6 @@ runwait(const char *cmd)
return -1;
}
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa), cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
return rc;
}
#define K_RUN 0 /* build+run BOTH, exits==wants, + byte-id */
#define K_BUILDERR 1 /* build FAILS with experr on BOTH drivers */
#define K_RUN_NOID 2 /* build+run BOTH, exits==wants, byte-id
* SKIPPED — cite the blocking task */
#define K_RUN_CS_WWERR 3 /* bound row: cstage build+run exit==cswant;
* wwstage build FAILS with experr — cite
* the blocking task */
struct row { const char *label; const char *src;
int cswant; int wwwant; int kind; const char *experr; };
/* errlog_has — a builderr cell must fail WITH its diagnostic; any other
* failure (parse error, crash, hang-kill) is a vacuous reject (940
* precedent). */
static int
errlog_has(const char *path, const char *needle)
{
@@ -128,390 +47,68 @@ errlog_has(const char *path, const char *needle)
return strstr(buf, needle) != NULL;
}
static const struct row rows[] = {
/* ---- c1: chain-membership graduations (were tag-0 silent) */
{ "chain_1lvl_i",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"export fn main() i32 = {\n"
" let s: base;\n"
" s.a = 4; s.b = 9;\n"
" let v: (void | al0) = s;\n"
" if (!(v is al0)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "chain_2lvl_i",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"type ali = al0;\n"
"export fn main() i32 = {\n"
" let s: base;\n"
" s.a = 4; s.b = 9;\n"
" let v: (void | ali) = s;\n"
" if (!(v is ali)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "chain_deep_src",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"export fn main() i32 = {\n"
" let s: ali2;\n"
" s.a = 4; s.b = 9;\n"
" let v: (void | ali) = s;\n"
" if (!(v is ali)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "chain_deep_var",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"export fn main() i32 = {\n"
" let s: base;\n"
" s.a = 4; s.b = 9;\n"
" let v: (void | ali2) = s;\n"
" if (!(v is ali2)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
/* CALL-src legs run 0/0; byte-id waived: the mk() frame carries
* the pre-existing #81 uninit-local zero-fill asm divergence
* (cells were byteid=NO at the #95 base too). */
{ "chain_call_bound81",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"fn mk() base = {\n"
" let s: base;\n"
" s.a = 4; s.b = 9;\n"
" return s;\n"
"};\n"
"export fn main() i32 = {\n"
" let v: (void | al0) = mk();\n"
" if (!(v is al0)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN_NOID, NULL }, /* byte-id: task #81 */
{ "chain_call2_bound81",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"type ali = al0;\n"
"fn mk() base = {\n"
" let s: base;\n"
" s.a = 4; s.b = 9;\n"
" return s;\n"
"};\n"
"export fn main() i32 = {\n"
" let v: (void | ali) = mk();\n"
" if (!(v is ali)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN_NOID, NULL }, /* byte-id: task #81 */
/* ≥2 chain hits — was a SILENT member-0 tag; twin texts share
* the experr tail (prefix convention). */
{ "chain_amb_loud",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"export fn main() i32 = {\n"
" let s: base;\n"
" s.a = 4; s.b = 9;\n"
" let v: (ali | ali2) = s;\n"
" if (v is ali) { return 1; };\n"
" if (v is ali2) { return 2; };\n"
" return 3;\n"
"};\n", 0, 0, K_BUILDERR,
"source alias chain reaches >=2 variants" },
/* pass-1 EXACT precedence pins: an exact source distinguishes
* (ali|ali2) — the chain arm must stay BEHIND pass 1. */
{ "chain_amb_srcA",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"export fn main() i32 = {\n"
" let s: ali;\n"
" s.a = 4; s.b = 9;\n"
" let v: (ali | ali2) = s;\n"
" if (!(v is ali)) { return 1; };\n"
" if (v is ali2) { return 2; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "chain_amb_srcB",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"export fn main() i32 = {\n"
" let s: ali2;\n"
" s.a = 4; s.b = 9;\n"
" let v: (ali | ali2) = s;\n"
" if (!(v is ali2)) { return 1; };\n"
" if (v is ali) { return 2; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
/* #218 nominal regression pins (rob §3e): (str | linerr) stays
* distinguishable through the new arm. */
{ "nom_str",
"package main;\n"
"type linerr = str;\n"
"export fn main() i32 = {\n"
" let s: str = \"ok\";\n"
" let v: (str | linerr) = s;\n"
" if (!(v is str)) { return 1; };\n"
" if (v is linerr) { return 2; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "nom_err",
"package main;\n"
"type linerr = str;\n"
"export fn main() i32 = {\n"
" let e: linerr = \"bad\";\n"
" let v: (str | linerr) = e;\n"
" if (!(v is linerr)) { return 1; };\n"
" if (v is str) { return 2; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "exact_ctl",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"type ali = al0;\n"
"export fn main() i32 = {\n"
" let s: ali;\n"
" s.a = 4; s.b = 9;\n"
" let v: (void | ali) = s;\n"
" if (!(v is ali)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
/* ---- pass-2 bare-source controls (the #15 leg stays put) */
{ "bare_ctl",
"package main;\n"
"type vt = struct { a: int };\n"
"type stream = *vt;\n"
"export fn main() i32 = {\n"
" let x: vt;\n"
" x.a = 7;\n"
" let v: (void | stream) = &x;\n"
" if (!(v is stream)) { return 1; };\n"
" match (v) {\n"
" case let s: stream => { if (s.a != 7) { return 2; }; };\n"
" case void => { return 3; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "bare_2lvl",
"package main;\n"
"type vt = struct { a: int };\n"
"type st0 = *vt;\n"
"type stream = st0;\n"
"export fn main() i32 = {\n"
" let x: vt;\n"
" x.a = 7;\n"
" let v: (void | stream) = &x;\n"
" if (!(v is stream)) { return 1; };\n"
" match (v) {\n"
" case let s: stream => { if (s.a != 7) { return 2; }; };\n"
" case void => { return 3; };\n"
" };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
{ "bare_ambig",
"package main;\n"
"type p1 = *i32;\n"
"type p2 = *i32;\n"
"export fn main() i32 = {\n"
" let x: i32 = 7;\n"
" let v: (p1 | p2) = &x;\n"
" return 0;\n"
"};\n", 0, 0, K_BUILDERR,
"bare source structurally matches >=2 NAMED variants" },
{ "bare_ambig2",
"package main;\n"
"type b1 = *i32;\n"
"type b2 = *i32;\n"
"type q1 = b1;\n"
"type q2 = b2;\n"
"export fn main() i32 = {\n"
" let x: i32 = 7;\n"
" let v: (q1 | q2) = &x;\n"
" return 0;\n"
"};\n", 0, 0, K_BUILDERR,
"bare source structurally matches >=2 NAMED variants" },
/* ww #277 aggregate-return capability leg — OUT of #95; dual
* cells pinned as observed so neither side drifts silently. */
{ "callret_bound277",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"type ali = al0;\n"
"fn mk() ali = {\n"
" let s: ali;\n"
" s.a = 4; s.b = 9;\n"
" return s;\n"
"};\n"
"export fn main() i32 = {\n"
" let v: (void | ali) = mk();\n"
" if (!(v is ali)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN_CS_WWERR,
"deferred #277" }, /* ww: task #277 */
/* ---- c2: structural fallback — was both-wrong-identical
* byte-id silent (live metric-1: checkers accept, cgen tagged
* 0). harec accepts by interning (one node, tag tb). */
{ "unrel_struct",
"package main;\n"
"type ta = struct { a: int, b: int };\n"
"type tb = struct { a: int, b: int };\n"
"export fn main() i32 = {\n"
" let s: ta;\n"
" s.a = 4; s.b = 9;\n"
" let v: (void | tb) = s;\n"
" if (!(v is tb)) { return 1; };\n"
" return 0;\n"
"};\n", 0, 0, K_RUN, NULL },
/* ---- c3 (#107): is/as ACCEPTANCE must stay nominal-exact. cgen's
* chain/structural tag-synthesis arms widen tag lookup at the WIDEN
* site only; the wwstage is/as gate (check.ww:4677) reuses
* flatvariantidxt and, pre-c3, the widening leaked into ACCEPTANCE
* (cs!=ww) and surfaced the >=2 cgen fatal mid-check. c3 added an
* exact-only mode for that caller. These pin BOTH stages REJECTING
* (cstage's independent gate check.c:2036 already does; wwstage now
* matches it) — NOT a crash for the ambiguity shape. Shared experr
* substring "not a variant" (cstage "X is not a variant of (...)",
* wwstage "is/as: not a variant of operand (X)"). */
{ "isas_chain_reject",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"fn f(v: (void | ali)) i32 = {\n"
" if (v is base) { return 1; };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { return 0; };\n",
0, 0, K_BUILDERR, "not a variant" },
{ "isas_unrel_reject",
"package main;\n"
"type ta = struct { a: int, b: int };\n"
"type tb = struct { a: int, b: int };\n"
"fn f(v: (void | tb)) i32 = {\n"
" if (v is ta) { return 1; };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { return 0; };\n",
0, 0, K_BUILDERR, "not a variant" },
{ "isas_amb_reject_notcrash",
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type ali = base;\n"
"type ali2 = ali;\n"
"fn f(v: (ali | ali2)) i32 = {\n"
" if (v is base) { return 1; };\n"
" return 0;\n"
"};\n"
"export fn main() i32 = { return 0; };\n",
0, 0, K_BUILDERR, "not a variant" },
/* ---- rob's obligated mixed prim/alias TWIN: union (int | ai)
* ai=int, source typed aj=int — under all-variants counting both the
* bare `int` variant and `ai` share the int bottom -> the cgen widen
* (full mode) hard-errors (harec nassign>=2 -> NULL). Pinned LOUD
* BOTH stages (the chain-ambiguity path; #95 deviation-2 ratified). */
{ "twin_prim_alias_amb",
"package main;\n"
"type ai = int;\n"
"type aj = int;\n"
"export fn main() i32 = {\n"
" let s: aj = 7;\n"
" let v: (int | ai) = s;\n"
" return 0;\n"
"};\n", 0, 0, K_BUILDERR,
"source alias chain reaches >=2 variants" },
};
/* kb5_v2sE: a CALL whose result is typed as the alias ali, widened into
* (void | ali). cstage's aggregate-return path runs it; wwstage #277 rejects. */
static const char *src =
"package main;\n"
"type base = struct { a: int, b: int };\n"
"type al0 = base;\n"
"type ali = al0;\n"
"fn mk() ali = {\n"
" let s: ali;\n"
" s.a = 4; s.b = 9;\n"
" return s;\n"
"};\n"
"export fn main() i32 = {\n"
" let v: (void | ali) = mk();\n"
" if (!(v is ali)) { return 1; };\n"
" return 0;\n"
"};\n";
static const char *experr = "deferred #277";
/* Build src with `driver`; if expect_err, the build must FAIL with experr on
* stderr. Otherwise build must succeed, then the binary runs and its exit must
* equal `want`. Returns 0 on the expected outcome, 1 otherwise. */
static int
run_driver(const char *driver, const struct row *r, int i, int expect_err,
int want)
check(const char *driver, int expect_err, int want)
{
char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160];
char cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/vc95_%d_d_%d", getpid(), i);
char tmpdir[96], srcp[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d", getpid(), expect_err);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/vc95_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/vc95_%d_%d", tmpdir, getpid(), i);
snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir);
snprintf(outbin, sizeof outbin, "%s/c", tmpdir);
snprintf(errf, sizeof errf, "%s/err", tmpdir);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -1; }
fputs(r->src, f);
FILE *f = fopen(srcp, "wb");
if (!f) { runwait(rmcmd); return 1; }
fputs(src, f);
fclose(f);
snprintf(cmd, sizeof cmd,
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
driver, outbin, src, errf);
snprintf(cmd, sizeof cmd, "timeout 20 %s build -o %s %s >/dev/null 2>%s",
driver, outbin, srcp, errf);
int brc = runwait(cmd);
int rc = 0;
if (expect_err) {
int ok = (brc != 0)
&& (r->experr == NULL || errlog_has(errf, r->experr));
if (!ok)
fprintf(stderr, "row[%s]: %s expected loud builderr "
"\"%s\" (brc=%d)\n", r->label, driver,
r->experr ? r->experr : "", brc);
runwait(rmcmd);
return ok ? 0 : 1;
if (brc == 0 || !errlog_has(errf, experr)) {
fprintf(stderr, "callret_bound277: %s expected loud "
"builderr \"%s\" (brc=%d)\n", driver, experr, brc);
rc = 1;
}
} else if (brc != 0) {
fprintf(stderr, "callret_bound277: build via %s failed\n", driver);
rc = 1;
} else {
int got = runwait(outbin);
if (got != want) {
fprintf(stderr, "callret_bound277: %s exit %d, want %d\n",
driver, got, want);
rc = 1;
}
}
if (brc != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
r->label, driver);
runwait(rmcmd);
return -1;
}
int got = runwait(outbin);
runwait(rmcmd);
if (got != want) {
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
r->label, driver, got, want);
return 1;
}
return 0;
}
static int
asm_byte_identical(const char *bin, const struct row *r, int i)
{
char src[96], cs[96], ws[96], cmd[1024];
snprintf(src, sizeof src, "/tmp/vc95_asm_%d_%d.ww", getpid(), i);
snprintf(cs, sizeof cs, "/tmp/vc95_asm_%d_%d_c.s", getpid(), i);
snprintf(ws, sizeof ws, "/tmp/vc95_asm_%d_%d_w.s", getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
unlink(src);
return -1;
}
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
bin, ws, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
unlink(src); unlink(cs);
return -1;
}
int rc = slurp_eq(cs, ws);
if (rc != 0)
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
r->label);
unlink(src); unlink(cs); unlink(ws);
return rc;
}
@@ -532,36 +129,15 @@ main(void)
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
total++;
int cs_err = rows[i].kind == K_BUILDERR;
if (run_driver(cdrv, &rows[i], i, cs_err,
rows[i].cswant) != 0) fail++;
}
if (access(wdrv, X_OK) == 0) {
for (int i = 0; i < n; i++) {
total++;
int ww_err = rows[i].kind == K_BUILDERR
|| rows[i].kind == K_RUN_CS_WWERR;
if (run_driver(wdrv, &rows[i], i, ww_err,
rows[i].wwwant) != 0) fail++;
}
for (int i = 0; i < n; i++) {
if (rows[i].kind != K_RUN)
continue;
total++;
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
}
}
int fail = 0;
fail += check(cdrv, 0, 0); /* cstage runs, exit 0 */
if (access(wdrv, X_OK) == 0)
fail += check(wdrv, 1, 0); /* wwstage loud-stops #277 */
if (fail) {
fprintf(stderr, "variant_chain_b95: %d/%d checks failed\n",
fail, total);
fprintf(stderr, "variant_chain_b95: %d checks failed\n", fail);
return 1;
}
printf("variant_chain_b95: %d/%d ok\n", total, total);
printf("variant_chain_b95: callret_bound277 pin ok\n");
return 0;
}

View File

@@ -1,287 +0,0 @@
/*
* 949_void_error_singleton_run — #140: a `!void` error-singleton used
* as a VALUE is a tag-only variant. Its void payload has size 0 and no
* storage, so cgexpr must materialise NOTHING — only the enclosing
* widen arm stamps the variant tag.
*
* Pre-fix cstage's cgexpr N_IDENT path fell through to the generic
* global-value load and emitted `MOVQ main.<name>(SB), AX` for a
* payload symbol that is never defined:
* w6l: undefined reference to 'main.too_long'
* wwstage already emitted tag-only (no symbol load) — the runtime-
* correct reference. The fix aligns cstage UP (ken-139 oracle).
*
* Every singleton-value position the cgexpr-ident root feeds is
* covered (enumerated at HEAD — all four link-failed identically):
* return, let-init, assign, call-arg. Rows also vary the union width
* (8B scalar payload vs a >8B str payload, which exercises the
* tag-return CX/R8 zeroing) and check BOTH the error-arm and the
* success-arm tags so a mis-stamped tag flips the exit code.
*
* Three checks per row:
* - byte-id: w6c vs w6c_ww .s must be identical (rule 10).
* - no-bogus-load: the cstage .s must NOT reference `<name>(SB)`
* for the singleton type-name (the pre-fix undefined symbol).
* - runtime: build via the ww / ww_ww drivers (the build step is
* the link, which is the pre-fix teeth) and run; the exit code
* pins the variant tag.
*
* This is half of the path-c1 pin (union with #141's def-dim file).
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want; /* expected exit code */
};
static const struct row rows[] = {
/* return-position: the void singleton returned from an 8B-payload
* union; match selects the singleton arm (exit 0). */
{ "return_singleton",
"type too_long = !void;\n"
"fn f() (i64 | too_long) = {\n"
" return too_long;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f()) {\n"
" case let v: i64 => return 1;\n"
" case too_long => return 0;\n"
" };\n"
" return 2;\n"
"};\n",
0 },
/* same fn shape, success variant: pins the tag discriminates
* (the success arm must run, returning the payload). */
{ "return_success",
"type too_long = !void;\n"
"fn f() (i64 | too_long) = {\n"
" return 42i64;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f()) {\n"
" case let v: i64 => { if (v != 42) { return 3; }; return 0; };\n"
" case too_long => return 1;\n"
" };\n"
" return 2;\n"
"};\n",
0 },
/* let-init: `let e: (i64 | too_long) = too_long;`. */
{ "letinit_singleton",
"type too_long = !void;\n"
"export fn main() i32 = {\n"
" let e: (i64 | too_long) = too_long;\n"
" match (e) {\n"
" case let v: i64 => return 1;\n"
" case too_long => return 0;\n"
" };\n"
" return 2;\n"
"};\n",
0 },
/* assign: reassign an existing union local to the void singleton
* (cg_widen_tagged_store source = the singleton ident). */
{ "assign_singleton",
"type too_long = !void;\n"
"export fn main() i32 = {\n"
" let e: (i64 | too_long) = 5i64;\n"
" e = too_long;\n"
" match (e) {\n"
" case let v: i64 => return 1;\n"
" case too_long => return 0;\n"
" };\n"
" return 2;\n"
"};\n",
0 },
/* call-arg: the singleton passed into a tagged parameter. */
{ "callarg_singleton",
"type too_long = !void;\n"
"fn use(r: (i64 | too_long)) i32 = {\n"
" match (r) {\n"
" case let v: i64 => return 1;\n"
" case too_long => return 0;\n"
" };\n"
" return 2;\n"
"};\n"
"export fn main() i32 = {\n"
" return use(too_long);\n"
"};\n",
0 },
/* wide union: a >8B (str) success payload makes the tagged return
* slot exceed one word, exercising the CX/R8 zeroing on the
* tag-only singleton return. Error arm selected. */
{ "return_singleton_wide",
"type too_long = !void;\n"
"fn f(ok: bool) (str | too_long) = {\n"
" if (ok) { return \"hello\"; };\n"
" return too_long;\n"
"};\n"
"export fn main() i32 = {\n"
" match (f(false)) {\n"
" case let s: str => return 1;\n"
" case too_long => { };\n"
" };\n"
" match (f(true)) {\n"
" case let s: str => { if (s.len != 5) { return 2; }; };\n"
" case too_long => return 3;\n"
" };\n"
" return 0;\n"
"};\n",
0 },
};
static const char *g_bin;
static int
compile_s(const char *tool, const char *src, const char *outpath,
const char *errpath)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2> %s",
g_bin, tool, src, outpath, errpath);
return runwait(cmd);
}
static int
file_eq(const char *a, const char *b)
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b);
return runwait(cmd) == 0;
}
static int
file_has(const char *path, const char *needle)
{
FILE *f = fopen(path, "rb");
if (!f) return 0;
static char buf[1 << 20];
size_t n = fread(buf, 1, sizeof buf - 1, f);
fclose(f);
buf[n] = '\0';
return strstr(buf, needle) != NULL;
}
static int
run_driver(const char *driver, const char *tmpdir, const char *src,
const char *label)
{
char cmd[1024], outbin[256];
snprintf(outbin, sizeof outbin, "%s/out_%s", tmpdir, driver);
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
g_bin, driver, outbin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: build via %s failed\n",
label, driver);
return -1;
}
return runwait(outbin);
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
static char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
g_bin = bin;
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
const struct row *r = &rows[i];
char tmpdir[128], rmcmd[160];
char src[160], cs_s[160], ww_s[160];
char cs_e[160], ww_e[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tves_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
snprintf(src, sizeof src, "%s/tves_%d_%d.ww",
tmpdir, getpid(), i);
snprintf(cs_s, sizeof cs_s, "%s/tves_%d_%d_cs.s",
tmpdir, getpid(), i);
snprintf(ww_s, sizeof ww_s, "%s/tves_%d_%d_ww.s",
tmpdir, getpid(), i);
snprintf(cs_e, sizeof cs_e, "%s/tves_%d_%d_cs.err",
tmpdir, getpid(), i);
snprintf(ww_e, sizeof ww_e, "%s/tves_%d_%d_ww.err",
tmpdir, getpid(), i);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return 1; }
fputs("package main;\n\n", f);
fputs(r->src, f);
fclose(f);
total++;
int cs_rc = compile_s("w6c", src, cs_s, cs_e);
int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e);
if (cs_rc != 0 || ww_rc != 0) {
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
"ww=%d\n", r->label, cs_rc, ww_rc);
fail++;
runwait(rmcmd);
continue;
}
if (!file_eq(cs_s, ww_s)) {
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
r->label);
fail++;
}
/* the pre-fix undefined-symbol signature: a `(SB)` load of
* the singleton type-name. Both unions name the singleton
* `too_long`; the wide row also has it. No row legitimately
* loads a `too_long(SB)` value (it is a type, not a global). */
if (file_has(cs_s, "too_long(SB)")) {
fprintf(stderr, "FAIL row[%s]: cstage still loads "
"the nonexistent void-singleton payload symbol\n",
r->label);
fail++;
}
int got_cs = run_driver("ww", tmpdir, src, r->label);
if (got_cs != r->want) {
fprintf(stderr, "FAIL row[%s] cstage: want %d "
"got %d\n", r->label, r->want, got_cs);
fail++;
}
char wwdrv[600];
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
if (access(wwdrv, X_OK) == 0) {
int got_ww = run_driver("ww_ww", tmpdir, src, r->label);
if (got_ww != r->want) {
fprintf(stderr, "FAIL row[%s] wwstage: "
"want %d got %d\n",
r->label, r->want, got_ww);
fail++;
}
}
runwait(rmcmd);
}
if (fail) {
fprintf(stderr, "void_error_singleton_run: %d/%d rows "
"failed\n", fail, total);
return 1;
}
printf("void_error_singleton_run: %d rows ok\n", total);
return 0;
}

View File

@@ -1,153 +0,0 @@
/*
* 989_globstructwiden_run — F8-c7 (report-item #50, #263): widening a module-
* GLOBAL struct ident into a tagged union (`let x: (S | str) = gs;`) must
* copy gs's full payload into the box, not just word0.
*
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* the struct-payload widen arm keyed off a local/literal-only struct-name
* lookup, so a module-global struct ident fell to the scalar word0 arm — only
* the first 8 bytes reached the box (wwstage), or the payload zero-filled
* (cstage, gs never read). The ww half landed in F8-c7; the cstage half (a
* global-struct-source branch — LEAQ g(SB),SI via aggarg_srcaddr, byte-copy
* into slot+8) landed in ww-core TASK #43. Both stages now copy the full
* payload and the .s is byte-identical.
*
* Rows assert the field-equality match returns 0 on both stages (cs==ww).
* row | shape | exit
* -----------+---------------------------------------------+-----
* widen_16 | gp:{a=5,b=6}; (pt|str)=gp; match pt fields | 0
* widen_24 | gp:{a=5,b=6,c=7}; (tri|str)=gp; match fields | 0
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_cs;
int want_ww;
};
static const struct row rows[] = {
{ "widen_16",
"package main;\n"
"type pt = struct { a: i64, b: i64 };\n"
"let gp: pt = pt { a = 5, b = 6 };\n"
"export fn main() int = {\n"
" let x: (pt | str) = gp;\n"
" match (x) {\n"
" case let q: pt => { if (q.a == 5 && q.b == 6) { return 0; }; return 2; };\n"
" case str => return 3;\n"
" };\n"
"};\n",
0, 0 },
{ "widen_24",
"package main;\n"
"type tri = struct { a: i64, b: i64, c: i64 };\n"
"let gp: tri = tri { a = 5, b = 6, c = 7 };\n"
"export fn main() int = {\n"
" let x: (tri | str) = gp;\n"
" match (x) {\n"
" case let q: tri => { if (q.a == 5 && q.b == 6 && q.c == 7) { return 0; }; return 2; };\n"
" case str => return 3;\n"
" };\n"
"};\n",
0, 0 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/gsw_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/gsw_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/gsw_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "globstructwiden_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
int is_ww = (d == 1);
for (int i = 0; i < n; i++) {
total++;
int want = is_ww ? rows[i].want_ww : rows[i].want_cs;
int got = run_build(drivers[d].drv, &rows[i], i);
if (got != want) {
fprintf(stderr, "globstructwiden_run[%s][%s]: exit=%d "
"want=%d\n", drivers[d].name, rows[i].label,
got, want);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "globstructwiden_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("globstructwiden_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,170 +0,0 @@
/*
* 989_globtagisas_run — F8-c3 (report-item #18): `is`/`as` on a module-
* GLOBAL tagged-union ident must read the tag/payload from g(SB), not the
* saved-BP word.
*
* THE BUG (cat-A silent miscompile): in cgenexpr.ww cgtypetest (`is`) and
* cgtypeassert (`as`) resolve an N_IDENT scrutinee's slot via localfindnode.
* A module-global ident returns nil (no BP slot), so scrutoff stayed 0 and
* the tag read fell on 0(BP) = the saved-BP word (and the payload on 8(BP) =
* the return address). cstage N_TYPETEST is uniformly cgexpr (MOVQ g(SB),AX),
* so the bootstrap corpus never trips it on 990-997 — a runtime row is the net.
*
* MIXED CATEGORY (per-half, asm-verified — spec §3 principle):
* • `is` half = ALIGN-UP. cstage reads the tag correctly (MOVQ g(SB),AX);
* wwstage read 0(BP). FIX routes the global ident to MOVQ g(SB),AX →
* cs==ww + runtime-correct (the is_* rows assert want on BOTH drivers).
* • `as` half = #263 BOTH-WRONG, now CLOSED both stages. cstage N_TYPEASSERT
* on a global ident spilled cgexpr's AX (tag only) plus UNINITIALIZED DX as
* the payload (never loaded g(SB)+8) → garbage (0). The ww half landed in
* F8-c3; the cstage half (copy the box words from g(SB)+0/+8[/+16] into the
* @asrt_spill so the tag-check + payload load index off memory like a local)
* landed in ww-core TASK #46. Both stages now read the real payload and the
* .s is byte-identical.
*
* Rows (built+run on cstage `ww` and wwstage `ww_ww`; cs==ww on every row):
* row | shape | exit | cat
* ----------+----------------------------------------+------+--------
* is_match | g:(i64|bool)=5; if g is i64 →0 else 12 | 0 | align-UP
* is_nomatch| g:(i64|bool)=5; if g is bool →13 else 0 | 0 | align-UP
* as_i64 | g:(i64|bool)=42; (g as i64):int | 42 | #46
* as_str | g:(str|i64)="hello"; len(g as str) | 5 | #46
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_cs;
int want_ww;
};
static const struct row rows[] = {
{ "is_match",
"package main;\n"
"let g: (i64 | bool) = 5;\n"
"fn check() int = { if (g is i64) { return 0; }; return 12; };\n"
"export fn main() int = { return check(); };\n",
0, 0 },
{ "is_nomatch",
"package main;\n"
"let g: (i64 | bool) = 5;\n"
"fn check() int = { if (g is bool) { return 13; }; return 0; };\n"
"export fn main() int = { return check(); };\n",
0, 0 },
/* #46 CLOSED both stages: cstage now copies the box from g(SB) into the
* @asrt_spill (was spilling uninitialized DX) → 42 == wwstage. */
{ "as_i64",
"package main;\n"
"let g: (i64 | bool) = 42;\n"
"fn check() int = { let x: i64 = g as i64; return x: int; };\n"
"export fn main() int = { return check(); };\n",
42, 42 },
/* #46 str variant — both stages copy the +16 cap word too. */
{ "as_str",
"package main;\n"
"let g: (str | i64) = \"hello\";\n"
"fn check() int = { let s: str = g as str; return len(s): int; };\n"
"export fn main() int = { return check(); };\n",
5, 5 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/gtia_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/gtia_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/gtia_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "globtagisas_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
int is_ww = (d == 1);
for (int i = 0; i < n; i++) {
total++;
int want = is_ww ? rows[i].want_ww : rows[i].want_cs;
int got = run_build(drivers[d].drv, &rows[i], i);
if (got != want) {
fprintf(stderr, "globtagisas_run[%s][%s]: exit=%d "
"want=%d\n", drivers[d].name, rows[i].label,
got, want);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "globtagisas_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("globtagisas_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,163 +0,0 @@
/*
* 989_globtagreassign_run — F8-c5 (report-item #32, #263): reassigning a
* module-GLOBAL tagged-union ident (`g = expr`) must store the new tag and
* payload, not clobber only one word.
*
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* cgassign's tagged-union reassignment arm delegated to the widener ONLY for a
* LOCAL ident. A module-global tagged ident had no arm and fell through to the
* generic scalar store (wwstage clobbered the tag word; cstage dropped the
* store entirely). The ww half landed in F8-c5; the cstage half (LEAQ g(SB),BX
* then cg_widen_tagged_store off BX) landed in ww-core TASK #41 — both stages
* now store the full box and the .s is byte-identical.
*
* Rows (cs==ww after the cstage half landed):
* row | shape | exit
* ----------------+----------------------------------------+-----
* reassign_i64 | g:(i64|void)=0; g=7; match i64 | 7
* reassign_str | g:(str|i64)=0; g="hello"; match str→len | 5
* reassign_union | g:(i64|bool)=0; g=5; match i64 | 5
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_cs;
int want_ww;
};
static const struct row rows[] = {
{ "reassign_i64",
"package main;\n"
"let g: (i64 | void) = 0;\n"
"export fn main() int = {\n"
" g = 7;\n"
" match (g) {\n"
" case let n: i64 => return n: int;\n"
" case void => return 99;\n"
" };\n"
"};\n",
7, 7 },
{ "reassign_str",
"package main;\n"
"let g: (str | i64) = 0;\n"
"export fn main() int = {\n"
" g = \"hello\";\n"
" match (g) {\n"
" case let s: str => return len(s): int;\n"
" case i64 => return 88;\n"
" };\n"
"};\n",
5, 5 },
{ "reassign_union",
"package main;\n"
"let g: (i64 | bool) = 0;\n"
"export fn main() int = {\n"
" g = 5;\n"
" match (g) {\n"
" case let n: i64 => return n: int;\n"
" case bool => return 77;\n"
" };\n"
"};\n",
5, 5 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/gtr_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/gtr_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/gtr_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "globtagreassign_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
int is_ww = (d == 1);
for (int i = 0; i < n; i++) {
total++;
int want = is_ww ? rows[i].want_ww : rows[i].want_cs;
int got = run_build(drivers[d].drv, &rows[i], i);
if (got != want) {
fprintf(stderr, "globtagreassign_run[%s][%s]: exit=%d "
"want=%d\n", drivers[d].name, rows[i].label,
got, want);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "globtagreassign_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("globtagreassign_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,163 +0,0 @@
/*
* 989_globtagwiden_run — F8-c8 (report-item #51, #263): widening a module-
* GLOBAL tagged-union ident into a wider union (`let x: (inner | str) = gi;`)
* must copy gi's whole box as the nested payload, not store its tag as the
* payload.
*
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
* the #218 nested-tagged-source arm was gated local-only, so a module-global
* tagged source fell to the scalar word0 widen arm — gi's TAG word landed as
* the box payload (wwstage), or frame garbage was copied, never gi(SB)
* (cstage). The ww half landed in F8-c8; the cstage half (the nested arm's
* N_IDENT copy grows a global branch — LEAQ gi(SB),SI via aggarg_srcaddr, copy
* the inner box into slot+8) landed in ww-core TASK #44. Both stages now copy
* the whole box and the .s is byte-identical.
*
* Rows assert the exact runtime-correct value on both stages (cs==ww).
* row | shape | exit
* ----------+-----------------------------------------+-----
* nest_47 | gi:inner=47; (inner|str)=gi; match→v | 47
* nest_99 | gi:inner=99; (inner|str)=gi; match→v | 99
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_ww;
};
static const struct row rows[] = {
{ "nest_47",
"package main;\n"
"type inner = (i64 | bool);\n"
"let gi: inner = 47;\n"
"export fn main() int = {\n"
" let x: (inner | str) = gi;\n"
" match (x) {\n"
" case let n: inner => {\n"
" match (n) {\n"
" case let v: i64 => return v: int;\n"
" case bool => return 88;\n"
" };\n"
" };\n"
" case str => return 3;\n"
" };\n"
"};\n",
47 },
{ "nest_99",
"package main;\n"
"type inner = (i64 | bool);\n"
"let gi: inner = 99;\n"
"export fn main() int = {\n"
" let x: (inner | str) = gi;\n"
" match (x) {\n"
" case let n: inner => {\n"
" match (n) {\n"
" case let v: i64 => return v: int;\n"
" case bool => return 88;\n"
" };\n"
" };\n"
" case str => return 3;\n"
" };\n"
"};\n",
99 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/gtw_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/gtw_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/gtw_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "globtagwiden_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
for (int i = 0; i < n; i++) {
total++;
int got = run_build(drivers[d].drv, &rows[i], i);
/* #44 CLOSED: cs==ww at the exact runtime-correct value
* on both drivers (the .s is byte-identical). */
if (got != rows[i].want_ww) {
fprintf(stderr, "globtagwiden_run[%s][%s]:"
" exit=%d want=%d\n", drivers[d].name,
rows[i].label, got, rows[i].want_ww);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "globtagwiden_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("globtagwiden_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,240 +0,0 @@
/*
* 989_taggedderefstore_run — #17 teeth (recorded by impl-write, 2026-06-13).
*
* Deref-target store into a tagged-union pointee: `*p = v` where p:*tagged.
* The plain-deref store arm sized the write off the pointee type and emitted
* a SINGLE fldstoreop (one MOVQ) — rhs landed in the tag word, the payload
* was dropped, and the union's discriminant was corrupted. A subsequent
* `match (v)` then dispatched on garbage.
*
* THIS WAS BOTH-STAGE-WRONG AND BYTE-ID-GREEN (#263-class, gate-blind): the
* cstage (cmd/w6c/cgen.c #17 deref else-branch) and wwstage (cgenexpr.ww
* #17 single-store tail) emitted IDENTICAL wrong asm, so the byte-id gates
* never caught it. #17 routes both stages' tagged-pointee deref store
* through the widener (cstage cg_widen_tagged_store / ww cgwidentaggedstore)
* via the shared tag scratch, then word-copies scratch -> *p — mirroring the
* proven index-element tagged arm (cstage cgen.c:6487 / ww cgenexpr.ww:8768).
* Scalar pointees keep the single-store path untouched.
*
* Rows (each program self-checks and returns 0 on all-correct, a 1-based
* code on the first mismatch; both stages build+run, rule-10, pin 0):
* taglo: *(int|bool), *p=42 — tag-low (int) variant, read back 42.
* taghi: *(int|bool), *p=true — tag-high (bool) variant survives.
* str3w: *(int|str), *p="hello" — a str payload is a 3-word {ptr,len,
* cap} header (slot = tag + 3 payload words); proves the
* multi-payload-word copy is COMPLETE, not just word0/word1.
* struct2w: *(int|pair), *p=pair{...} — a 16B struct payload (slot = tag +
* 2 payload words, ssz 24); proves the mid-count copy loop moves
* BOTH payload words and the widener's struct-source arm fires.
* nullfold: *(*int|void), *p=&a — nullable fold (1-word slot, pointer IS
* the disc); the folded payload path widens correctly.
* scalarctl:*int, *p=42 — scalar control: the non-tagged single-store
* path is UNCHANGED (no scratch, no widen).
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_exit; /* >= 0: pin the absolute value; -1: cs==ww only */
};
static const struct row rows[] = {
/* taglo — tag-low (int) variant via *p=42; read back the int. */
{ "taglo",
"package main;\n"
"export fn main() int = {\n"
" let v: (int | bool) = false;\n"
" let p: *(int | bool) = &v;\n"
" *p = 42;\n"
" match (v) {\n"
" case let n: int => { if (n != 42) { return 1; }; return 0; };\n"
" case bool => { return 2; };\n"
" };\n"
"};\n",
0 },
/* taghi — tag-high (bool) variant via *p=true; the tag must remap. */
{ "taghi",
"package main;\n"
"export fn main() int = {\n"
" let v: (int | bool) = 0;\n"
" let p: *(int | bool) = &v;\n"
" *p = true;\n"
" match (v) {\n"
" case int => { return 1; };\n"
" case let b: bool => { if (!b) { return 2; }; return 0; };\n"
" };\n"
"};\n",
0 },
/* str3w — str payload (3-word header); the >2-word payload copy must
* be complete. Pre-fix only the tag word (or one payload word) moved. */
{ "str3w",
"package main;\n"
"export fn main() int = {\n"
" let v: (int | str) = 0;\n"
" let p: *(int | str) = &v;\n"
" *p = \"hello\";\n"
" match (v) {\n"
" case int => { return 1; };\n"
" case let s: str => { if (s.len: int != 5) { return 2; }; return 0; };\n"
" };\n"
"};\n",
0 },
/* struct2w — an aggregate (struct) payload: a 16B pair → slot = tag +
* 2 payload words (ssz 24, 3-word copy loop). Exercises the widener's
* struct-source arm through the deref path and proves the mid-count
* copy loop moves BOTH payload words, not word0 alone. */
{ "struct2w",
"package main;\n"
"type pair = struct { a: i64, b: i64 };\n"
"export fn main() int = {\n"
" let v: (int | pair) = 0;\n"
" let p: *(int | pair) = &v;\n"
" *p = pair { a = 0x1111, b = 0x2222 };\n"
" match (v) {\n"
" case int => { return 1; };\n"
" case let q: pair => {\n"
" if (q.a != 0x1111) { return 2; };\n"
" if (q.b != 0x2222) { return 3; };\n"
" return 0;\n"
" };\n"
" };\n"
"};\n",
0 },
/* nullfold — nullable fold (1-word slot): the pointer is the disc. */
{ "nullfold",
"package main;\n"
"export fn main() int = {\n"
" let a: int = 7;\n"
" let v: (*int | void) = void;\n"
" let p: *(*int | void) = &v;\n"
" *p = &a;\n"
" match (v) {\n"
" case let q: *int => { if (*q != 7) { return 1; }; return 0; };\n"
" case void => { return 2; };\n"
" };\n"
"};\n",
0 },
/* scalarctl — non-tagged pointee: the single-store path is unchanged. */
{ "scalarctl",
"package main;\n"
"export fn main() int = {\n"
" let v: int = 3;\n"
" let p: *int = &v;\n"
" *p = 42;\n"
" if (v != 42) { return 1; };\n"
" return 0;\n"
"};\n",
0 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], cmd[1024], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagderef_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/tagderef_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/tagderef_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
total++;
int gc = run_build(cdrv, &rows[i], i);
if (gc < 0) {
fprintf(stderr, "taggedderefstore_run[cstage][%s]: build/run "
"failed (got %d)\n", rows[i].label, gc);
fail++;
continue;
}
if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) {
fprintf(stderr, "taggedderefstore_run[cstage][%s]: exit=%d "
"want=%d (tag corrupt / payload dropped)\n",
rows[i].label, gc, rows[i].want_exit);
fail++;
}
if (!have_ww) {
fprintf(stderr, "taggedderefstore_run: skip wwstage (no %s)\n",
wdrv);
continue;
}
int gw = run_build(wdrv, &rows[i], i);
if (gw != gc) {
fprintf(stderr, "taggedderefstore_run[%s]: cs=%d != ww=%d "
"(#17 deref-store divergence)\n",
rows[i].label, gc, gw);
fail++;
}
if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) {
fprintf(stderr, "taggedderefstore_run[wwstage][%s]: exit=%d "
"want=%d (tag corrupt / payload dropped)\n",
rows[i].label, gw, rows[i].want_exit);
fail++;
}
}
if (fail) {
fprintf(stderr, "taggedderefstore_run: %d/%d checks failed\n",
fail, total);
return 1;
}
printf("taggedderefstore_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,248 +0,0 @@
/*
* 989_taggedglobalindex_run — #16 teeth (recorded by impl-16, 2026-06-14).
*
* Indexed store into a GLOBAL array of a tagged-union type: `gs[i] = v` where
* gs : [N](A | B). The indexed-tagged-element assign arm (cmd/w6c/cgen.c
* ~6502) computed its base address with only LOCAL/frame variants — it LACKED
* the `isglobal -> LEAQ name(SB)` branch the SCALAR `arr[i] = v` element arm
* (~6767) already had. For a global tagged array the base resolved to a junk
* BP-relative offset (off==0), so the widened store landed nowhere observable;
* a later `match (gs[i])` then read the unchanged static-init slot. cstage
* silently returned 0 (WRONG); wwstage (cgenexpr.ww index arm, already carries
* the globalname branch) returned the correct value — a cs!=ww divergence the
* byte-id gate could SEE. #16 aligns cstage UP: it adds the mirrored
* `isglobal && is_arr -> LEAQ name(SB)` / `isglobal -> MOVQ name(SB)` branch to
* the indexed-tagged-elem arm. cstage-only; no .ww source touched.
*
* Rows (each program self-checks and returns 0 on all-correct, a 1-based code
* on the first mismatch; both stages build+run, rule-10, pin 0):
* glo_taglo: global [N](int|bool), gs[1]=42 — tag-low (int) variant, read
* back 42. The literal #16 teeth (cstage pre-fix gave 0).
* glo_taghi: global [N](int|bool), gs[1]=true — tag-high (bool) variant;
* the tag must remap through the widener.
* glo_varidx: global [N](int|bool), runtime index gs[i]=99 — the scaled-
* index path (IMULQ) layered on the global base.
* glo_str3w: global [N](int|str), gs[1]="hello" — a str payload is a
* 3-word {ptr,len,cap} header; proves the multi-word slot copy
* over the global base is COMPLETE, not just word0/word1.
* glo_struct2w:global [N](int|pair), gs[1]=pair{...} — a 16B struct payload
* (slot = tag + 2 payload words); proves the mid-count copy loop
* moves BOTH payload words over the global base.
* loc_ctl: LOCAL [N](int|bool), a[1]=42 — control for the frame-base
* path that ALREADY worked; must stay correct + byte-id.
* scalar_ctl: scalar global [N]int, arr[1]=42 — control for the template
* arm (the scalar element store) the fix was mirrored from.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_exit; /* >= 0: pin the absolute value; -1: cs==ww only */
};
static const struct row rows[] = {
/* glo_taglo — the #16 teeth: global tagged array, int variant store. */
{ "glo_taglo",
"package main;\n"
"let gs: [2](int | bool) = [0, 0];\n"
"export fn main() int = {\n"
" gs[1] = 42;\n"
" match (gs[1]) {\n"
" case let n: int => { if (n != 42) { return 1; }; return 0; };\n"
" case bool => { return 2; };\n"
" };\n"
"};\n",
0 },
/* glo_taghi — bool variant via gs[1]=true; the tag must remap. */
{ "glo_taghi",
"package main;\n"
"let gs: [2](int | bool) = [0, 0];\n"
"export fn main() int = {\n"
" gs[1] = true;\n"
" match (gs[1]) {\n"
" case int => { return 1; };\n"
" case let b: bool => { if (!b) { return 2; }; return 0; };\n"
" };\n"
"};\n",
0 },
/* glo_varidx — runtime index over the global base (scaled IMULQ). */
{ "glo_varidx",
"package main;\n"
"let gs: [3](int | bool) = [0, 0, 0];\n"
"export fn main() int = {\n"
" let i: int = 2;\n"
" gs[i] = 99;\n"
" match (gs[2]) {\n"
" case let n: int => { if (n != 99) { return 1; }; return 0; };\n"
" case bool => { return 2; };\n"
" };\n"
"};\n",
0 },
/* glo_str3w — str payload (3-word header); the multi-word slot copy
* over the global base must be complete. */
{ "glo_str3w",
"package main;\n"
"let gs: [2](int | str) = [0, 0];\n"
"export fn main() int = {\n"
" gs[1] = \"hello\";\n"
" match (gs[1]) {\n"
" case int => { return 1; };\n"
" case let s: str => { if (s.len: int != 5) { return 2; }; return 0; };\n"
" };\n"
"};\n",
0 },
/* glo_struct2w — a 16B struct payload (slot = tag + 2 payload words);
* proves the mid-count copy loop moves BOTH payload words. */
{ "glo_struct2w",
"package main;\n"
"type pair = struct { a: i64, b: i64 };\n"
"let gs: [2](int | pair) = [0, 0];\n"
"export fn main() int = {\n"
" gs[1] = pair { a = 0x1111, b = 0x2222 };\n"
" match (gs[1]) {\n"
" case int => { return 1; };\n"
" case let q: pair => {\n"
" if (q.a != 0x1111) { return 2; };\n"
" if (q.b != 0x2222) { return 3; };\n"
" return 0;\n"
" };\n"
" };\n"
"};\n",
0 },
/* loc_ctl — LOCAL tagged array: the frame-base path that already worked
* must stay correct (the fix must not perturb the non-global branch). */
{ "loc_ctl",
"package main;\n"
"export fn main() int = {\n"
" let a: [2](int | bool) = [0, 0];\n"
" a[1] = 42;\n"
" match (a[1]) {\n"
" case let n: int => { if (n != 42) { return 1; }; return 0; };\n"
" case bool => { return 2; };\n"
" };\n"
"};\n",
0 },
/* scalar_ctl — scalar global array: the template arm the fix mirrors. */
{ "scalar_ctl",
"package main;\n"
"let arr: [3]int = [0, 0, 0];\n"
"export fn main() int = {\n"
" arr[1] = 42;\n"
" if (arr[1] != 42) { return 1; };\n"
" return 0;\n"
"};\n",
0 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit code,
* or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], cmd[1024], rmcmd[160];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagglobidx_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/tagglobidx_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/tagglobidx_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
int have_ww = (access(wdrv, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
total++;
int gc = run_build(cdrv, &rows[i], i);
if (gc < 0) {
fprintf(stderr, "taggedglobalindex_run[cstage][%s]: build/run "
"failed (got %d)\n", rows[i].label, gc);
fail++;
continue;
}
if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) {
fprintf(stderr, "taggedglobalindex_run[cstage][%s]: exit=%d "
"want=%d (global base dropped / store lost)\n",
rows[i].label, gc, rows[i].want_exit);
fail++;
}
if (!have_ww) {
fprintf(stderr, "taggedglobalindex_run: skip wwstage (no %s)\n",
wdrv);
continue;
}
int gw = run_build(wdrv, &rows[i], i);
if (gw != gc) {
fprintf(stderr, "taggedglobalindex_run[%s]: cs=%d != ww=%d "
"(#16 indexed-global-tagged base divergence)\n",
rows[i].label, gc, gw);
fail++;
}
if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) {
fprintf(stderr, "taggedglobalindex_run[wwstage][%s]: exit=%d "
"want=%d (global base dropped / store lost)\n",
rows[i].label, gw, rows[i].want_exit);
fail++;
}
}
if (fail) {
fprintf(stderr, "taggedglobalindex_run: %d/%d checks failed\n",
fail, total);
return 1;
}
printf("taggedglobalindex_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,191 +0,0 @@
/*
* 989_taggedidx_run — F7-c6 (#23): a tagged-union element indexed off a
* CALL or SLICE base must load the full tag+payload cursor, both stages.
*
* THE BUG (cat-A silent miscompile, gate-blind): cgindex
* (selfhost/cmd/wcc/cgenexpr.ww) classifies an indexed element as tagged
* so it loads the (tag, val0, val1) slot. The non-ident-base arm gated
* that classify on a base-kind WHITELIST (N_DOT / N_INDEX / N_UN-deref),
* so a tagged element indexed off an N_CALL base (`f()[i]`) or N_SLICE
* base (`s[a:b][i]`) fell through to the scalar load — a lone
* `MOVQ (AX),AX` (tag word only), dropping the payload. cstage classifies
* for ANY base off the stamped element tinfo (`esubu->kind == TY_TAGGED`,
* cmd/w6c/cgen.c:8101), so it loaded the full cursor and ran correct
* (cs rc=50 / ww rc=40 in the review repro — the cat-A divergence).
* 990-997 stay green because the corpus never indexes a tagged element
* off a call/slice base; only a runtime row catches it. THE FIX: drop the
* base-kind whitelist — classify the tagged element off n.type_ for any
* non-N_IDENT base, aligning wwstage UP.
*
* Arrays of tagged are built by per-element store; box = (i64|bool).
*
* Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value):
* row | shape | want
* -----------------+--------------------------------+------
* slice_tagged_idx | arr[1:][0] (N_SLICE base) | 40 [#23 bug]
* call_tagged_idx | getrows()[1] (N_CALL base) | 50 [#23 bug]
* ident_tagged_idx | arr[1] (N_IDENT base) | 40 (control: the
* | first arm already handled it — no regression)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_exit;
};
static const struct row rows[] = {
/* (1) #23 — tagged element off a SLICE base `arr[1:][0]`. arr[1] is the
* i64 variant 40; the bug loaded only the tag word → garbage match. */
{ "slice_tagged_idx",
"package main;\n"
"type box = (i64 | bool);\n"
"export fn main() int = {\n"
" let arr: [3]box;\n"
" arr[0] = 10; arr[1] = 40; arr[2] = 70;\n"
" let v: box = arr[1:][0];\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case let x: i64 => { r = x; };\n"
" case let b: bool => { r = 99; };\n"
" };\n"
" return r: int;\n"
"};\n",
40 },
/* (2) #23 — tagged element off a CALL base `getrows()[1]`. The fn
* returns []box; element 1 is the i64 variant 50. */
{ "call_tagged_idx",
"package main;\n"
"type box = (i64 | bool);\n"
"fn getrows() []box = {\n"
" let arr: [3]box;\n"
" arr[0] = 10; arr[1] = 50; arr[2] = 70;\n"
" return arr[0:];\n"
"};\n"
"export fn main() int = {\n"
" let v: box = getrows()[1];\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case let x: i64 => { r = x; };\n"
" case let b: bool => { r = 99; };\n"
" };\n"
" return r: int;\n"
"};\n",
50 },
/* (3) control — tagged element off an N_IDENT base `arr[1]` (the first
* cgindex arm already handled it via istaggedtype on the base tnode):
* c6 must not regress it. arr[1] == 40. */
{ "ident_tagged_idx",
"package main;\n"
"type box = (i64 | bool);\n"
"export fn main() int = {\n"
" let arr: [3]box;\n"
" arr[0] = 10; arr[1] = 40; arr[2] = 70;\n"
" let v: box = arr[1];\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case let x: i64 => { r = x; };\n"
" case let b: bool => { r = 99; };\n"
" };\n"
" return r: int;\n"
"};\n",
40 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagidx_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/tagidx_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/tagidx_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "taggedidx_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
for (int i = 0; i < n; i++) {
total++;
int got = run_build(drivers[d].drv, &rows[i], i);
if (got != rows[i].want_exit) {
fprintf(stderr, "taggedidx_run[%s][%s]: exit=%d "
"want=%d\n", drivers[d].name, rows[i].label,
got, rows[i].want_exit);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "taggedidx_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("taggedidx_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -1,265 +0,0 @@
/*
* 989_tagnorm_run — F1 (#1/#3): tagged-union type-set normalization must be
* symmetric across stages. cstage resolve_type N_TTAGGED (cmd/wcc/check.c:
* 801-882) drops `never`, dedups variants (variant_match: NAMED nominal,
* else structural), collapses a lone survivor to that variant, and folds a
* two-variant `(*T|void)` to an 8B nullable pointer. The wwstage formerly
* skipped ALL four at BOTH size sources — astsize (selfhost/cmd/wcc/check.ww)
* and tinfofornode N_TTAGGED — so size()/align() folded the RAW declaration
* list and tag numbering ran off it too.
*
* THE BUG (cat-A silent miscompile, gate-blind): size((*u8|void)) folded
* 16 in ww vs 8 in cs; size((i32|never)) 16 vs 4; align((i32|never)) 8 vs 4;
* size((i32|i32)) 16 vs 4 — all rc=0 both stages, divergent constant baked
* into the .s (MOVQ $16 vs $8/$4). The corpus never declares a never/dup/
* collapsible/nullable union whose SIZE it folds, so 990-997 stayed green;
* only a runtime size-fold row catches it.
*
* THE FIX: ONE normalization site — tinfofornode N_TTAGGED (the tinfo SSoT
* cgen already reads via ti.params/ti.size) gained never-drop + dedup +
* single-collapse; astsize/astalign N_TTAGGED delegate to it (mirroring the
* existing N_TTUPLE arm). cgen's deduped tag numbering rides ti.params for
* free.
*
* Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value):
* row | shape | want | pre-fix ww
* ---------------+-------------------------------+------+-----------
* nullable_size | size((*u8|void)) | 8 | 16 [#1]
* never_size | size((i32|never)) | 4 | 16 [#1]
* dup_size | size((i32|i32)) | 4 | 16 [#1]
* never_align | align((i32|never)) | 4 | 8 [#1]
* dedup_match | "hi": (i32|i32|str), match str | 7 | 7 (green
* | both — dedup keeps construct/match consistent; the
* | tag-NUMBER divergence #3 is byte-id, caught by B1)
* void_dedup_ret | bare `return;` of (i32|i32|void)| 5 | !5 [voidvar-
* | then match the void variant | | iantindex fold]
* void_pos0_ret | bare `return;` of (void|i32) | 5 | 5 (idx-0
* | | | boundary)
* void_pos2_ret | bare `return;` of (i32|str|void)| 5 | !5 [idx-2
* | | | teeth]
*
* void_{dedup,pos0,pos2}_ret pin voidvariantindex's idx counter at the
* three normalized positions (0/1/2): the first fold omitted `idx += 1`,
* so it returned 0 for ANY void position — only positions 1 and 2 are RED
* under that bug (position 0 is the one value it happened to get right, a
* boundary pin). void_dedup_ret is the gate-blind teeth for the cgenutil
* voidvariantindex
* fold: the bare-`return;` void tag must come off the NORMALIZED (deduped)
* variant chain, like construct/match already do. With the F1 checker dedup
* (i32|i32|void)->(i32|void) but a RAW-list voidvariantindex, bare return
* stores tag 2 while match expects void at tag 1 -> no arm fires, r stays 0
* (RED). The fold reads ti.params -> tag 1 -> the void arm yields 5.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
int want_exit;
};
static const struct row rows[] = {
/* (1) #1 — nullable `(*T|void)` folds to an 8B pointer slot, not a
* 16B tag+payload box. */
{ "nullable_size",
"package main;\n"
"type p = (*u8 | void);\n"
"export fn main() int = { return size(p): int; };\n",
8 },
/* (2) #1 — `never` drops, leaving a lone i32 → the union collapses to
* i32 (size 4), not an 8+8 box. */
{ "never_size",
"package main;\n"
"type q = (i32 | never);\n"
"export fn main() int = { return size(q): int; };\n",
4 },
/* (3) #1 — duplicate variants dedup to one i32 → collapse to i32. */
{ "dup_size",
"package main;\n"
"type d = (i32 | i32);\n"
"export fn main() int = { return size(d): int; };\n",
4 },
/* (4) #1 — align() rides the same normalization: the collapsed i32
* aligns to 4, not the tag word's 8. */
{ "never_align",
"package main;\n"
"type q = (i32 | never);\n"
"export fn main() int = { return align(q): int; };\n",
4 },
/* (5) #3 — a deduped `(i32|i32|str)` still constructs+matches its str
* variant correctly in BOTH stages (the str-arm yields 7). Pins
* cs==ww on the functional outcome; the per-stage tag-NUMBER (str=$1
* vs $2) divergence is rule-10 byte-id, proven by B1 self-compile. */
{ "dedup_match",
"package main;\n"
"type u = (i32 | i32 | str);\n"
"export fn main() int = {\n"
" let v: u = \"hi\";\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case let s: str => { r = 7; };\n"
" case let x: i32 => { r = 1; };\n"
" };\n"
" return r: int;\n"
"};\n",
7 },
/* (6) #3/F1-fold — a deduped `(i32|i32|void)` returned via bare
* `return;` must tag the void variant off the NORMALIZED chain (idx 1),
* matching the dedup'd construct/match numbering. A raw-list
* voidvariantindex tags it 2 -> no match arm fires -> r stays 0. The
* void arm yields 5. */
{ "void_dedup_ret",
"package main;\n"
"type u = (i32 | i32 | void);\n"
"fn f() u = { return; };\n"
"export fn main() int = {\n"
" let v: u = f();\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case void => { r = 5; };\n"
" case let x: i32 => { r = 9; };\n"
" };\n"
" return r: int;\n"
"};\n",
5 },
/* (7) #3/F1-fold — void at NORMALIZED index 0 pins the lower
* boundary of voidvariantindex's idx counter (the only position the
* missing `idx += 1` happened to get right). `(void|i32)` keeps void
* first; bare `return;` tags idx 0, the void arm yields 5. */
{ "void_pos0_ret",
"package main;\n"
"type u = (void | i32);\n"
"fn f() u = { return; };\n"
"export fn main() int = {\n"
" let v: u = f();\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case void => { r = 5; };\n"
" case let x: i32 => { r = 9; };\n"
" };\n"
" return r: int;\n"
"};\n",
5 },
/* (8) #3/F1-fold — void at NORMALIZED index 2 is the stronger teeth
* for the idx counter: a raw-list / non-incrementing voidvariantindex
* tags it 0 (the i32 arm) instead of 2, so r never reaches 5.
* `(i32|str|void)` has no dedup/drop, so void's normalized index IS
* its declaration index 2. */
{ "void_pos2_ret",
"package main;\n"
"type u = (i32 | str | void);\n"
"fn f() u = { return; };\n"
"export fn main() int = {\n"
" let v: u = f();\n"
" let r: i64 = 0;\n"
" match (v) {\n"
" case void => { r = 5; };\n"
" case let s: str => { r = 8; };\n"
" case let x: i32 => { r = 9; };\n"
" };\n"
" return r: int;\n"
"};\n",
5 },
};
/* run_build — build+run `src` via `driver`; returns the binary's exit
* code, or -1 on a build failure. */
static int
run_build(const char *driver, const struct row *r, int i)
{
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagnorm_%d_d_%d", getpid(), i);
mkdir(tmpdir, 0755);
snprintf(src, sizeof src, "%s/tagnorm_%d_%d.ww", tmpdir, getpid(), i);
snprintf(outbin, sizeof outbin, "%s/tagnorm_%d_%d", tmpdir, getpid(), i);
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
FILE *f = fopen(src, "wb");
if (!f) { runwait(rmcmd); return -2; }
fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
driver, outbin, src);
int brc = runwait(cmd);
int got = -1;
if (brc == 0) got = runwait(outbin);
runwait(rmcmd);
return brc == 0 ? got : -1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cdrv[1024], wdrv[1024];
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
struct { const char *name; const char *drv; int gated; }
drivers[] = {
{ "cstage", cdrv, 0 },
{ "wwstage", wdrv, 1 },
{ NULL, NULL, 0 },
};
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int d = 0; drivers[d].name; d++) {
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
fprintf(stderr, "tagnorm_run: skip %s (no %s)\n",
drivers[d].name, drivers[d].drv);
continue;
}
for (int i = 0; i < n; i++) {
total++;
int got = run_build(drivers[d].drv, &rows[i], i);
if (got != rows[i].want_exit) {
fprintf(stderr, "tagnorm_run[%s][%s]: exit=%d "
"want=%d\n", drivers[d].name, rows[i].label,
got, rows[i].want_exit);
fail++;
}
}
}
if (fail) {
fprintf(stderr, "tagnorm_run: %d/%d fixtures failed\n",
fail, total);
return 1;
}
printf("tagnorm_run: %d/%d ok\n", total, total);
return 0;
}

View File

@@ -0,0 +1,14 @@
//ww:error "#58: >32B tagged-field indexed store unreachable until #114"
// 944_idx_tagged_field big_assign tripwire: a >32B box STORE via xs[i].m
// LOUD-STOPS in BOTH stages until #114. A SUCCESS = loud-stop regressed to silent.
package main;
type big = (void | size | [5]size);
type row = struct { id: int, m: big };
export fn main() i32 = {
let xs: [2]row = [
row { id = 1, m = 7: size },
row { id = 2, m = 8: size },
];
xs[0].m = 9: size;
return 0;
};

View File

@@ -0,0 +1,16 @@
//ww:error "#58: >32B tagged-field indexed read unreachable until #114"
// 944_idx_tagged_field big_read tripwire: a >32B box read via the indexed spine
// (xs[i].m) LOUD-STOPS in BOTH stages (rule 7) until #114 wires >32B box memcpy.
// Reachable via a NARROW init; a SUCCESS here = the loud-stop regressed to silent.
package main;
type big = (void | size | [5]size);
type row = struct { id: int, m: big };
export fn main() i32 = {
let xs: [2]row = [
row { id = 1, m = 7: size },
row { id = 2, m = 8: size },
];
let v: size = xs[0].m as size;
if (v != 7) { return 1; };
return 0;
};

View File

@@ -0,0 +1,14 @@
//ww:error "#58: float-payload tagged-field indexed store unreachable until #114"
// 944_idx_tagged_field float_assign tripwire: a float-payload box STORE via
// xs[i].m LOUD-STOPS in BOTH stages until #114. SUCCESS = loud-stop regressed.
package main;
type fo = (void | f64);
type row = struct { id: int, m: fo };
export fn main() i32 = {
let xs: [2]row = [
row { id = 1, m = 1.0: f64 },
row { id = 2, m = 2.0: f64 },
];
xs[0].m = 3.0: f64;
return 0;
};

View File

@@ -0,0 +1,14 @@
//ww:error "#58: multi-word tagged-field indexed store unreachable until #114"
// 944_idx_tagged_field midword_assign tripwire: a multi-word (>16B, <=32B box)
// STORE via xs[i].m LOUD-STOPS in BOTH stages until #114. SUCCESS = regressed.
package main;
type mid = (void | size | [3]size);
type row = struct { id: int, m: mid };
export fn main() i32 = {
let xs: [2]row = [
row { id = 1, m = 7: size },
row { id = 2, m = 8: size },
];
xs[0].m = 9: size;
return 0;
};

View File

@@ -0,0 +1,16 @@
//ww:error "structural fallback cannot disambiguate nominally-distinct same-shape variants"
// 925_nested_union_widen collision_guard: two distinct names a,b both !(i32|bool)
// in one union (a | b) — without nominal layout the widen cannot pick a variant.
// BOTH stages must loud-stop (runww ERROR arm runs w6c AND w6c_ww). Migrated from
// 989-era 925_nested_union_widen_run.c (#5-C5).
package main;
type a = !(i32 | bool);
type b = !(i32 | bool);
export fn main() i32 = {
let x: a = (1i32: a);
let r: (a | b) = x;
match (r) {
case let p: a => return 1;
case let q: b => return 2;
};
};

View File

@@ -0,0 +1,15 @@
//ww:error "source alias chain reaches >=2 variants"
// 944_variant_chain_b95 chain_amb_loud: source base reaches both ali and ali2
// (chain) — ambiguous without nominal layout (#95). BOTH stages loud-stop.
package main;
type base = struct { a: int, b: int };
type ali = base;
type ali2 = ali;
export fn main() i32 = {
let s: base;
s.a = 4; s.b = 9;
let v: (ali | ali2) = s;
if (v is ali) { return 1; };
if (v is ali2) { return 2; };
return 3;
};

View File

@@ -0,0 +1,11 @@
//ww:error "bare source structurally matches >=2 NAMED variants"
// 944_variant_chain_b95 bare_ambig: a bare *i32 source matches both p1 and p2
// (#15/#218). BOTH stages loud-stop.
package main;
type p1 = *i32;
type p2 = *i32;
export fn main() i32 = {
let x: i32 = 7;
let v: (p1 | p2) = &x;
return 0;
};

View File

@@ -0,0 +1,13 @@
//ww:error "bare source structurally matches >=2 NAMED variants"
// 944_variant_chain_b95 bare_ambig2: bare *i32 reaches q1,q2 via b1/b2 chains
// (#15/#218). BOTH stages loud-stop.
package main;
type b1 = *i32;
type b2 = *i32;
type q1 = b1;
type q2 = b2;
export fn main() i32 = {
let x: i32 = 7;
let v: (q1 | q2) = &x;
return 0;
};

View File

@@ -0,0 +1,13 @@
//ww:error "not a variant"
// 944_variant_chain_b95 isas_amb_reject_notcrash: `is base` on (ali | ali2)
// (both reach base) — must REJECT (not crash) on the ambiguity (#107). BOTH
// stages reject with the diagnostic body, not a signal.
package main;
type base = struct { a: int, b: int };
type ali = base;
type ali2 = ali;
fn f(v: (ali | ali2)) i32 = {
if (v is base) { return 1; };
return 0;
};
export fn main() i32 = { return 0; };

View File

@@ -0,0 +1,12 @@
//ww:error "not a variant"
// 944_variant_chain_b95 isas_chain_reject: `is base` on (void | ali) where
// ali=base — is/as ACCEPTANCE stays nominal-exact (#107). BOTH stages reject
// (cstage "base is not a variant of (...)", wwstage "is/as: not a variant of...").
package main;
type base = struct { a: int, b: int };
type ali = base;
fn f(v: (void | ali)) i32 = {
if (v is base) { return 1; };
return 0;
};
export fn main() i32 = { return 0; };

View File

@@ -0,0 +1,12 @@
//ww:error "not a variant"
// 944_variant_chain_b95 isas_unrel_reject: `is ta` on (void | tb) where ta,tb
// are same-shape but distinct names — is/as acceptance is nominal-exact (#107).
// BOTH stages reject.
package main;
type ta = struct { a: int, b: int };
type tb = struct { a: int, b: int };
fn f(v: (void | tb)) i32 = {
if (v is ta) { return 1; };
return 0;
};
export fn main() i32 = { return 0; };

View File

@@ -0,0 +1,12 @@
//ww:error "source alias chain reaches >=2 variants"
// 944_variant_chain_b95 twin_prim_alias_amb: union (int | ai) ai=int, source aj=int
// — both the bare int variant and ai share the int bottom, ambiguous (#95
// deviation-2). BOTH stages loud-stop.
package main;
type ai = int;
type aj = int;
export fn main() i32 = {
let s: aj = 7;
let v: (int | ai) = s;
return 0;
};