/* * 941_slice_store_cap_run — runtime coverage for task #7: a `[]T` (slice-typed) * VALUE stored through an indexed / field / chained lhs, and read back, must * move the full 24B {ptr,len,cap} header, not just {ptr}. Slices are 24B always; * the G-cluster's store+read arms were kind-gated on str ONLY (the slice arm was * never extended), so a slice value fell to the 1-word fldstoreop/fldloadop * default and silently DROPPED len+cap. str IS []u8 since Phase 2 (#1), so the * str 3-word machinery applies to slices verbatim — the fix widens each gate * from `str` to `str || slice` (kind-OR, never a sz==24 test, which would also * catch >16B structs). * * Sites exercised (cstage cmd/w6c/cgen.c + cgenexpr.ww twin), each store paired * with its read mirror: * A whole-element `xs[i] = sl` + read `xs[i]` (cgen.c store ~3650/3692; * read N_INDEX cgslicehdr; ww cgassign elemtn + cgindex elemisslice). * B arr[i].field `arr[i].f = sl` + read `arr[i].f` (G1-twin store; cgdot * arrfield read). * C chained *struct `r.sym.f = sl` + read `r.sym.f` (G2-twin store; the * chained-*struct read was already 3-word). * D value-spine `o.i.f = sl` + read `o.i.f` (value-struct-spine * store; the value-spine read was already 3-word). * * UNLIKE the str store probes (937/938), here BOTH the store AND the read were * 1-word before the fix, so each row is a store->read-back ROUNDTRIP: a 1-word * store leaves the dst's len/cap at their prior value, and a 1-word read never * loads them, so a broken stage yields a value whose len/cap are not p's. The * full {ptr,len,cap} triple is asserted (ptr via s[0]=='h'=104, len=2, cap=8; * cap!=len so a dropped len OR cap is caught). * * POISON: rows B/C/D seed the dst slot with a DIFFERENT slice q (len=4,cap=5) * via a PROVEN 3-word slice store that is NOT the site under test — the single- * dot field store, which was merged onto the slice arm pre-#7 (cgen.c C4.4): * B via `pr.f = q` (pr=&arr[1], *struct field); * C via `st.f = q` (direct field); * D via `pi.f = q` (pi=&o.i, via-ptr field). * So before the fix the dst keeps q's cap=5 while the read returns stale words; * either way cap != 8. Row A is a bare roundtrip with no explicit poison: no * proven 3-word store targets a bare slice array-element header (the same reason * str has no standalone whole-element-store probe), so the store+read pair is * itself the discriminator. * * Slices are built with explicit pseudo-field stores (`s.ptr=&buf[0]; s.len=N; * s.cap=M`), the proven construction used by 691 — NOT sub-slicing, whose cap is * the separate #20 fix that depends on this fold. * * Verified fail-before (all 4 rows exit 1, cap reads the poison/stale, not 8) / * pass-after (exit 0) on BOTH the cstage `ww` and wwstage `ww_ww` drivers. * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling * race does not apply (mirrors the 928/932 precedent). */ #include #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; } struct row { const char *label; const char *src; int want; }; static const struct row rows[] = { /* A — whole-element `xs[0] = p` into a [2][]u8 local array, read back * via `xs[0]`. Exercises the N_INDEX store arm (elem_is_slice) and the * N_INDEX read (cgslicehdr) together. No proven poison for a bare * array-element header; the roundtrip is the probe. */ { "slice_store_wholeelem", "export fn main() i32 = {\n" " let hb: [8]u8; hb[0] = 104u8;\n" " let p: []u8; p.ptr = &hb[0]; p.len = 2; p.cap = 8;\n" " let xs: [2][]u8;\n" " xs[0] = p;\n" " let e: []u8 = xs[0];\n" " if (e.cap: i32 != 8) { return 1; };\n" " if (e.len: i32 != 2) { return 2; };\n" " if (e[0] != 104u8) { return 3; };\n" " return 0;\n" "};\n", 0 }, /* B — `arr[i].f = p` into a [N]rec field (G1-twin). Poison arr[1].f * (cap=5,len=4,'q') via &arr[1] + the proven single-dot *struct field * store; then the field-of-indexed store lands p (cap=8,len=2,'h'). */ { "slice_store_arrfield", "type rec = struct { f: []u8 };\n" "export fn main() i32 = {\n" " let qb: [8]u8; qb[0] = 113u8;\n" " let hb: [8]u8; hb[0] = 104u8;\n" " let q: []u8; q.ptr = &qb[0]; q.len = 4; q.cap = 5;\n" " let p: []u8; p.ptr = &hb[0]; p.len = 2; p.cap = 8;\n" " let arr: [3]rec;\n" " let pr: *rec = &arr[1];\n" " pr.f = q;\n" " arr[1].f = p;\n" " let s: []u8 = arr[1].f;\n" " if (s.cap: i32 != 8) { return 1; };\n" " if (s.len: i32 != 2) { return 2; };\n" " if (s[0] != 104u8) { return 3; };\n" " return 0;\n" "};\n", 0 }, /* C — chained `r.sym.f = p` where r.sym is a *inner (G2-twin). Poison * the pointee field via the proven direct field store (`st.f = q`); the * chained store derefs r.sym and overwrites st.f. */ { "slice_store_chained_ptr", "type inner = struct { f: []u8 };\n" "type outer = struct { sym: *inner };\n" "export fn main() i32 = {\n" " let qb: [8]u8; qb[0] = 113u8;\n" " let hb: [8]u8; hb[0] = 104u8;\n" " let q: []u8; q.ptr = &qb[0]; q.len = 4; q.cap = 5;\n" " let p: []u8; p.ptr = &hb[0]; p.len = 2; p.cap = 8;\n" " let st: inner;\n" " st.f = q;\n" " let r: outer;\n" " r.sym = &st;\n" " r.sym.f = p;\n" " let s: []u8 = r.sym.f;\n" " if (s.cap: i32 != 8) { return 1; };\n" " if (s.len: i32 != 2) { return 2; };\n" " if (s[0] != 104u8) { return 3; };\n" " return 0;\n" "};\n", 0 }, /* D — value-spine `o.i.f = p` where o.i is a value-struct field (not a * pointer). Poison o.i.f via &o.i + the proven via-ptr field store * (`pi.f = q`); the value-spine store walks o.i and overwrites .f. */ { "slice_store_valuespine", "type inner = struct { f: []u8 };\n" "type outer = struct { i: inner };\n" "export fn main() i32 = {\n" " let qb: [8]u8; qb[0] = 113u8;\n" " let hb: [8]u8; hb[0] = 104u8;\n" " let q: []u8; q.ptr = &qb[0]; q.len = 4; q.cap = 5;\n" " let p: []u8; p.ptr = &hb[0]; p.len = 2; p.cap = 8;\n" " let o: outer;\n" " let pi: *inner = &o.i;\n" " pi.f = q;\n" " o.i.f = p;\n" " let s: []u8 = o.i.f;\n" " if (s.cap: i32 != 8) { return 1; };\n" " if (s.len: i32 != 2) { return 2; };\n" " if (s[0] != 104u8) { return 3; };\n" " return 0;\n" "};\n", 0 }, }; static int run_driver(const char *driver, const struct row *r, int i) { char src[96], tmpdir[96], cmd[1024]; snprintf(src, sizeof src, "/tmp/slicestore_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/slicestore_%d_d_%d", getpid(), i); FILE *f = fopen(src, "wb"); if (!f) return -1; fputs(r->src, f); fclose(f); mkdir(tmpdir, 0755); snprintf(cmd, sizeof cmd, "cd %s && %s build %s", tmpdir, driver, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); unlink(src); rmdir(tmpdir); return -1; } const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char outbin[160]; snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); char *dot = strrchr(outbin, '.'); if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; int got = runwait(outbin); unlink(src); unlink(outbin); rmdir(tmpdir); 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, "slice_store_cap_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, "slice_store_cap_run[%s][%s]: exit=%d want=%d\n", drivers[d].name, rows[i].label, got, rows[i].want); fail++; } } } if (fail) { fprintf(stderr, "slice_store_cap_run: %d/%d fixtures failed\n", fail, total); return 1; } printf("slice_store_cap_run: %d/%d ok\n", total, total); return 0; }