/* * 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 #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; }; #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; }