/* * 953_arraytoslice_run — runtime + byte-id + reject net for #258: the * implicit [N]T -> []T array-to-slice BORROW. Hare admits an array with a * defined length wherever its element slice is expected (assign / return / * call-arg / init), as a borrow — `.ptr = &arr[0], .len = N, .cap = N` * (ref/harec/src/types.c:1080-1097, the SLICE-dst arm). ww previously * REJECTED it everywhere (cmd/wcc/type.c:#108(c) exclusion); base64 * worked around the gap with explicit `a[0:n]` slices. * * Fix (DESUGAR, checker-only, ZERO new cgen): type_assignable / * isassignable admit array→slice on an exact element match; the four * acceptance sites (clet / N_ASSIGN / call-arg gather / N_RETURN) then * rewrite the array expr to the explicit full slice `arr[0:len(arr)]` (an * N_SLICE over the array base) via the shared desugar_arrayslice / * desugararrayslice helper. cgen is untouched — the existing slice * lowering (#252/#257/#135 made array bases, incl struct-field arrays, * correct) materialises the borrow header, identically in both stages. * * Positive rows: cstage `ww build` + run for exit code (the four contexts * + a BORROW proof — mutate through the slice, observe the change in the * backing array, i.e. alias not copy), then w6c vs w6c_ww `.s` cmp for the * rule-10 byte-id gate (all four contexts emit byte-identical asm). * - let_i32 let-init borrow `let s: []i32 = a`, s[2] → 33 * - let_u8 let-init borrow, u8 element, s[3] → 66 * - assign_i32 assignment borrow `s = a` (s was a[0:1]), s[3] → 4 * - callarg_i32 call-arg borrow `sum(a)` (sums all elements) → 65 * - callarg_u8 call-arg borrow, u8 element → 70 * - return_i32 return borrow `return g` (g a global array) → 17 * - borrow_i32 mutate s[2]=55 through the slice, read a[2] → 55 * * Reject rows (cs==ww symmetric loud-reject): an element-type MISMATCH * ([4]i32 -> []u8) is NOT a borrow — both stages must refuse it. w6c and * w6c_ww are each run directly and must exit non-zero (the desugar's * element-exact gate; type_assignable / isassignable fall through to a * confident reject). * - mismatch_let let s: []u8 = a where a:[4]i32 → both reject * - mismatch_callarg f(a) where f wants []u8, a:[4]i32 → both reject */ #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"), *fb = fopen(b, "rb"); if (fa == NULL || fb == NULL) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } int ca, cb, eq = 0; do { ca = fgetc(fa); cb = fgetc(fb); if (ca != cb) { eq = -1; break; } } while (ca != EOF); fclose(fa); fclose(fb); return eq; } struct row { const char *label; const char *src; int want_exit; int reject; }; static const struct row rows[] = { { "let_i32", "package main;\n" "export fn main() i32 = {\n" " let a: [4]i32 = [11, 22, 33, 44];\n" " let s: []i32 = a;\n" " return s[2];\n" "};\n", 33, 0 }, { "let_u8", "package main;\n" "export fn main() i32 = {\n" " let a: [4]u8 = [11u8, 22u8, 33u8, 66u8];\n" " let s: []u8 = a;\n" " return s[3]: i32;\n" "};\n", 66, 0 }, { "assign_i32", "package main;\n" "export fn main() i32 = {\n" " let a: [4]i32 = [1, 2, 3, 4];\n" " let s: []i32 = a[0:1];\n" " s = a;\n" " return s[3];\n" "};\n", 4, 0 }, { "callarg_i32", "package main;\n" "fn sum(s: []i32) i32 = {\n" " let t: i32 = 0; let i: i32 = 0;\n" " for (i < s.len) { t += s[i]; i += 1; };\n" " return t;\n" "};\n" "export fn main() i32 = {\n" " let a: [4]i32 = [10, 20, 30, 5];\n" " return sum(a);\n" "};\n", 65, 0 }, { "callarg_u8", "package main;\n" "fn sum(s: []u8) i32 = {\n" " let t: i32 = 0; let i: i32 = 0;\n" " for (i < s.len) { t += s[i]: i32; i += 1; };\n" " return t;\n" "};\n" "export fn main() i32 = {\n" " let a: [3]u8 = [10u8, 20u8, 40u8];\n" " return sum(a);\n" "};\n", 70, 0 }, { "return_i32", "package main;\n" "let g: [3]i32 = [7, 8, 9];\n" "fn mk() []i32 = { return g; };\n" "export fn main() i32 = {\n" " let s: []i32 = mk();\n" " return s[1] + s[2];\n" "};\n", 17, 0 }, { "borrow_i32", "package main;\n" "export fn main() i32 = {\n" " let a: [4]i32 = [1, 2, 3, 4];\n" " let s: []i32 = a;\n" " s[2] = 55;\n" " return a[2];\n" "};\n", 55, 0 }, /* Reject rows: element-type mismatch — both stages refuse. */ { "mismatch_let", "package main;\n" "export fn main() i32 = {\n" " let a: [4]i32 = [1, 2, 3, 4];\n" " let s: []u8 = a;\n" " return 0;\n" "};\n", 0, 1 }, { "mismatch_callarg", "package main;\n" "fn f(s: []u8) i32 = { return 0; };\n" "export fn main() i32 = {\n" " let a: [4]i32 = [1, 2, 3, 4];\n" " return f(a);\n" "};\n", 0, 1 }, { NULL, NULL, 0, 0 }, }; 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 w6c[1100], w6c_ww[1100]; snprintf(w6c, sizeof w6c, "%s/w6c", bin); snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); if (access(w6c_ww, X_OK) != 0) { fprintf(stderr, "arraytoslice: w6c_ww missing — cannot run the " "cs==ww byte-id gate (the whole point of this test)\n"); return 1; } int n = 0, fail = 0; for (int i = 0; rows[i].src; i++, n++) { char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwa2s_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); char rmcmd[128]; snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); char src[128]; snprintf(src, sizeof src, "%s/wwa2s_%d_%d.ww", tmpdir, getpid(), i); FILE *f = fopen(src, "wb"); if (f == NULL) { fail++; runwait(rmcmd); continue; } fputs(rows[i].src, f); fclose(f); char cs_s[128], ws_s[128]; snprintf(cs_s, sizeof cs_s, "%s/wwa2s_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwa2s_%d_%d_ww.s", tmpdir, getpid(), i); char cmd[2048]; if (rows[i].reject) { /* Element mismatch — both stages must refuse (non-zero). */ snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) == 0) { fprintf(stderr, "row[%s]: w6c ACCEPTED a " "type-mismatch array→slice (must reject)\n", rows[i].label); fail++; } 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 ACCEPTED a " "type-mismatch array→slice (must reject)\n", rows[i].label); fail++; } runwait(rmcmd); continue; } /* Positive row: cstage build + run for exit code. */ char outbin[128]; snprintf(outbin, sizeof outbin, "%s/wwa2s_%d_%d", tmpdir, getpid(), i); snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: cstage build failed\n", rows[i].label); fail++; runwait(rmcmd); continue; } int got = runwait(outbin); if (got != rows[i].want_exit) { fprintf(stderr, "row[%s]: cstage exit %d, want %d\n", rows[i].label, got, rows[i].want_exit); fail++; } /* rule-10 byte-id: w6c vs w6c_ww .s. */ 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", rows[i].label); fail++; runwait(rmcmd); continue; } 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", rows[i].label); fail++; runwait(rmcmd); continue; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER (rule-10 " "byte-id violation)\n", rows[i].label); fail++; } runwait(rmcmd); } if (fail) { fprintf(stderr, "%d/%d array-to-slice tests failed\n", fail, n); return 1; } printf("arraytoslice: %d/%d ok (cstage run + cs==ww byte-id + reject)\n", n, n); return 0; }