/* * 945_tuple_lit_declblind_run — #64 + #68: a tuple LITERAL fills the * register cursor DECL-BLIND. The #57 decl wire (a declared-tagged element's * concrete rvalue widens into its box) stopped at N_LET / N_RETURN; the * other two tuple-literal consumers — destructure-REASSIGN (N_MASSIGN, #64) * and CALL-ARG send (#68) — still rode the decl-less route, so a declared- * tagged element was stored/sent WORD-0 ONLY (the box tag never set) and the * cursor skewed every later element. Both stages, #263 gate-blind: the asm * was byte-identical cs==ww and both ran wrong, so only RUNTIME catches it. * * Fix (both stages, byte-identical per rule 10): N_MASSIGN builds a DECLARED * tuple type from the lvalue binding types and threads it into the cursor * fill + receive (a `_` lvalue, never type-stamped, falls back to the rhs * literal element type for its stride); the call-arg send/restage/drain key * on the PARAM tuple type (the literal's element-constructed type undercounts * a wide tagged box). The single residual generic cgexpr(N_TUPLE) arm is * provably non-widening (the constructed type IS the governing type there). * * Every RUN row reads the box PAYLOAD back (binds n and asserts its VALUE), * not merely which match arm fired — a skew that sets the tag right but * corrupts the payload still fails. Rows (RUN unless marked ERR): * massign_tagged `a,b=(7i64,99)` into a:(i32|i64); n==7 ⇒ 1, else 3; * wrong arm ⇒ 0; garbage-tag fall-through ⇒ 2 (base). * callarg_tagged `f((7i64,99))` param (ev,i64); match t.0, n==7 ⇒ 1. * base: garbage tag ⇒ fall-through ⇒ 2. * massign_blank `_,a=(99,7i64)` — `_` stride + named box widen; the box * is element 1 (off the `_`-aligned cursor). n==7 ⇒ 1. * base: word-0-only store corrupts a's tag ⇒ fall-through ⇒ * 2 (the box's tag sits at word0; the lone scalar word * overwrites it — verified on pristine abd97e6). * callarg_drain `g((7i64, 35))` param (ev,i64); g returns (box payload) * + t.1. This is the #68 DRAIN teeth (ken's verified probe * shape): the param-aware drain must pop the box's 2 words * AND the trailing i64 together. base: box is word-0 only * so the i64 arm never fires (payload 0) ⇒ 0 + 35 = 35; * post: 7 + 35 = 42. base 35 ≠ 42, and the +35 simultaneously * proves the trailing arg still lands in its reg. * nested_tuple_arg [ERR] `h(((1,2),3))` param ((i32,i32),i64): a NESTED- * TUPLE tuple-arg element has no transport and stays rule-7 * LOUD (the fix graduates ONLY a declared-tagged element; * the TY_TUPLE/STRUCT/ARRAY arms are preserved). Asserts the * build FAILS (nonzero) on BOTH drivers — locks the * preserved loudness so a future widen-everything regresses. * * RUN rows: build+run on BOTH drivers AND cs==ww .s byte-identical (rule 10). * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling * race does not apply (903/940/945 precedent). */ #include #include #include #include #include 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; } /* builderr: the build must FAIL (rule-7 loud); RUN rows leave it 0. */ struct row { const char *label; const char *src; int want; int builderr; }; static const struct row rows[] = { { "massign_tagged", "package main;\n" "type ev = (i32 | i64);\n" "export fn main() i32 = {\n" " let a: ev = 5i32;\n" " let b: i64 = 0;\n" " a, b = (7i64, 99);\n" " match (a) {\n" " case let n: i64 => { if (n == 7) { return 1; }; return 3; };\n" " case let m: i32 => { return 0; };\n" " };\n" " return 2;\n" "};\n", 1, 0 }, { "callarg_tagged", "package main;\n" "type ev = (i32 | i64);\n" "fn f(t: (ev, i64)) i32 = {\n" " match (t.0) {\n" " case let n: i64 => { if (n == 7) { return 1; }; return 3; };\n" " case let m: i32 => { return 0; };\n" " };\n" " return 2;\n" "};\n" "export fn main() i32 = {\n" " return f((7i64, 99));\n" "};\n", 1, 0 }, { "massign_blank", "package main;\n" "type ev = (i32 | i64);\n" "export fn main() i32 = {\n" " let a: ev = 5i32;\n" " _, a = (99, 7i64);\n" " match (a) {\n" " case let n: i64 => { if (n == 7) { return 1; }; return 3; };\n" " case let m: i32 => { return 0; };\n" " };\n" " return 2;\n" "};\n", 1, 0 }, { "callarg_drain", "package main;\n" "type ev = (i32 | i64);\n" "fn g(t: (ev, i64)) i32 = {\n" " let bp: i64 = 0;\n" " match (t.0) {\n" " case let n: i64 => { bp = n; };\n" " case let m: i32 => { bp = 0; };\n" " };\n" " return (bp + t.1): i32;\n" "};\n" "export fn main() i32 = {\n" " return g((7i64, 35));\n" "};\n", 42, 0 }, { "nested_tuple_arg", "package main;\n" "fn h(t: ((i32, i32), i64)) i32 = {\n" " return 0;\n" "};\n" "export fn main() i32 = {\n" " return h(((1, 2), 3));\n" "};\n", 0, 1 }, }; /* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */ 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/tldb_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); snprintf(src, sizeof src, "%s/tldb_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/tldb_%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); /* ERR row: the rule-7 loud arm must fire — build FAILS (nonzero). A * clean build means the preserved loudness regressed. */ if (r->builderr) { int ok = (brc != 0); if (!ok) fprintf(stderr, "row[%s]: %s built clean, expected " "rule-7 loud build-fail\n", r->label, driver); runwait(rmcmd); return ok ? 0 : 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 != 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 byteid(const char *w6c, const char *w6c_ww, const struct row *r, int i) { char src[96], cs_s[96], ws_s[96], cmd[1024]; snprintf(src, sizeof src, "/tmp/tldb_bi_%d_%d.ww", getpid(), i); snprintf(cs_s, sizeof cs_s, "/tmp/tldb_bi_%d_%d_cs.s", getpid(), i); snprintf(ws_s, sizeof ws_s, "/tmp/tldb_bi_%d_%d_ww.s", getpid(), i); FILE *f = fopen(src, "wb"); if (!f) return -1; fputs(r->src, f); fclose(f); int rc = 0; snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c failed\n", r->label); rc = 1; } else { snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, ws_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: w6c_ww failed\n", r->label); rc = 1; } else if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER " "(rule-10 byte-id)\n", r->label); rc = 1; } } unlink(src); unlink(cs_s); unlink(ws_s); return rc; } 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], w6c[640], w6c_ww[640]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); struct { const char *name; const char *path; 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].path, X_OK) != 0) { fprintf(stderr, "tuple_lit_declblind: skip %s (no %s)\n", drivers[d].name, drivers[d].path); continue; } for (int i = 0; i < n; i++) { total++; if (run_driver(drivers[d].path, &rows[i], i) != 0) fail++; } } if (access(w6c_ww, X_OK) == 0) { for (int i = 0; i < n; i++) { if (rows[i].builderr) continue; /* no .s to compare */ total++; if (byteid(w6c, w6c_ww, &rows[i], i) != 0) fail++; } } if (fail) { fprintf(stderr, "tuple_lit_declblind: %d/%d checks failed\n", fail, total); return 1; } printf("tuple_lit_declblind: %d/%d ok\n", total, total); return 0; }