/* * 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 #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[] = { /* 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[96], tmpdir[96], cmd[1024]; snprintf(src, sizeof src, "/tmp/msv_run_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/msv_run_%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, "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; }