/* * 951_arrlit_elem_narrow_run — #251: narrow int/rune array-literal * elements to a declared [N]T element type, at the let / def / * struct-field init sites that #130 (test 920) left unwired. * * Pre-#251 state (verified by building both stages): * - cstage REJECTED in-range bare-int/rune array lits at the LOCAL * let, def, and struct-field sites ("init [4]i32 not assignable to * declared [4]u8"): the array-literal element type came from the * elements via type_default (int-lit→i32, rune-lit→rune) with no * declared-element-type propagation. Only the MODULE-level let site * (#130) accepted. * - wwstage was UNEVEN: local/module let correct, but the DEF path ran * NO init-assignability check and the N_STRUCTLIT head-stamp left * field assignability parked (#23) — so def + struct-field SILENTLY * over-accepted out-of-range and str→u8 elements (a rule-7 * miscompile: out-of-range truncates). * * #251 fix = the int/rune analogue of coerce_floatlit, realised as the * EXISTING #130 accept-if-fits range-check (NOT a node-type restamp — * cgen drives the element WIDTH from the DECLARED type, so a restamp * would be dead code). Both stages now, at let/def/struct-field: * - foldable int/rune literal → defcastfits range-check vs declared T * (in-range accept; out-of-range REJECT loud, rule-7/Drew). * - non-foldable → type_assignable / isassignable to the element type. * - cstage: arrlit_init_fits wired at check.c clet (local let), * struct-field-init, and def-init. * - wwstage: checkarrlitfits (factored from checkletassign's inline * #130 block) called from the let path, the def path, and a TARGETED * array-field walk in the N_STRUCTLIT arm (the array-field * accept-if-fits ONLY — isolated from the parked #23 field walk). * * Element width is declared-type-driven at cgen, so the array literal * node keeps its [N]i32 / [N]rune type and no restamp is needed; the * cs==ww byte-id gate confirms the emitted bytes are u8-wide regardless. * * Accept rows (run + cs==ww byte-id), int and rune, across the 3 sites: * - let_int `let a:[4]u8=[65,66,67,68]; a[0]` → 65 * - let_rune `let a:[4]u8=['A','B','C','D']; a[0]` → 65 * - def_int `def D:[4]u8=[65,..]; D[0]` → 65 * - def_rune `def D:[4]u8=['A',..]; D[0]` → 65 * - struct_int `enc{m=[65,..]}; E.m[0]` → 65 * - struct_rune `enc{m=['A',..]}; E.m[1]` → 66 * * Reject rows (must FAIL build on BOTH stages — rule-7 loud reject). * Two reject branches per the predicate: the FOLDABLE range-check * (out-of-range int) and the NON-FOLDABLE element-type check (str→u8), * each exercised at all 3 sites. (A rune>u8 over-range row is not * expressible: the ww lexer caps rune escapes at \xFF=255 and does not * decode multi-byte UTF-8 in a rune literal, so every rune literal * already fits u8 — the foldable branch is identical for int and rune.) * - let_over `let a:[2]u8=[300,1]` (foldable: out of range) * - def_over `def D:[2]u8=[300,1]` (foldable: was a ww over-accept) * - struct_over `enc{m=[300,1]}` (foldable: was a ww over-accept) * - let_str `let a:[2]u8=["x","y"]` (non-foldable: str→u8) * - def_str `def D:[2]u8=["x","y"]` (non-foldable: was a ww over-accept) * - struct_str `enc{m=["x","y"]}` (non-foldable: was a ww over-accept) */ #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 arow { const char *label; const char *src; int want_exit; }; struct rrow { const char *label; const char *src; }; static const struct arow accept_rows[] = { { "let_int", "package main;\n" "export fn main() i32 = {\n" " let a: [4]u8 = [65, 66, 67, 68];\n" " return a[0]: i32;\n" "};\n", 65 }, { "let_rune", "package main;\n" "export fn main() i32 = {\n" " let a: [4]u8 = ['A', 'B', 'C', 'D'];\n" " return a[0]: i32;\n" "};\n", 65 }, { "def_int", "package main;\n" "def D: [4]u8 = [65, 66, 67, 68];\n" "export fn main() i32 = { return D[0]: i32; };\n", 65 }, { "def_rune", "package main;\n" "def D: [4]u8 = ['A', 'B', 'C', 'D'];\n" "export fn main() i32 = { return D[0]: i32; };\n", 65 }, { "struct_int", "package main;\n" "type enc = struct { m: [4]u8 };\n" "let E: enc = enc { m = [65, 66, 67, 68] };\n" "export fn main() i32 = { return E.m[0]: i32; };\n", 65 }, { "struct_rune", "package main;\n" "type enc = struct { m: [4]u8 };\n" "export fn main() i32 = {\n" " let e: enc = enc { m = ['A', 'B', 'C', 'D'] };\n" " return e.m[1]: i32;\n" "};\n", 66 }, { NULL, NULL, 0 } }; static const struct rrow reject_rows[] = { { "let_over", "package main;\n" "export fn main() i32 = { let a: [2]u8 = [300, 1]; return a[0]: i32; };\n" }, { "def_over", "package main;\n" "def D: [2]u8 = [300, 1];\n" "export fn main() i32 = { return D[0]: i32; };\n" }, { "struct_over", "package main;\n" "type enc = struct { m: [2]u8 };\n" "let E: enc = enc { m = [300, 1] };\n" "export fn main() i32 = { return E.m[0]: i32; };\n" }, { "let_str", "package main;\n" "export fn main() i32 = { let a: [2]u8 = [\"x\", \"y\"]; return a[0]: i32; };\n" }, { "def_str", "package main;\n" "def D: [2]u8 = [\"x\", \"y\"];\n" "export fn main() i32 = { return 0; };\n" }, { "struct_str", "package main;\n" "type enc = struct { m: [2]u8 };\n" "let E: enc = enc { m = [\"x\", \"y\"] };\n" "export fn main() i32 = { return E.m[0]: i32; };\n" }, { NULL, NULL } }; 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); int cb = fgetc(fb); if (ca != cb) { rc = -1; break; } if (ca == EOF) break; } fclose(fa); fclose(fb); return rc; } 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, "arrnarrow: w6c_ww missing — cannot run the " "cs==ww gate (the whole point of this test)\n"); return 1; } int n = 0, fail = 0; /* Accept rows: cstage build + run + cs==ww byte-id. */ for (int i = 0; accept_rows[i].src; i++, n++) { char tmpdir[64]; snprintf(tmpdir, sizeof tmpdir, "/tmp/wwan_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); char rmcmd[160]; snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); char src[128], outbin[128]; snprintf(src, sizeof src, "%s/wwan_%d_%d.ww", tmpdir, getpid(), i); snprintf(outbin, sizeof outbin, "%s/wwan_%d_%d", tmpdir, getpid(), i); FILE *f = fopen(src, "wb"); if (f == NULL) { fail++; runwait(rmcmd); continue; } fputs(accept_rows[i].src, f); fclose(f); char cmd[2048]; snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src); if (runwait(cmd) != 0) { fprintf(stderr, "accept[%s]: cstage build failed\n", accept_rows[i].label); fail++; runwait(rmcmd); continue; } int got = runwait(outbin); if (got != accept_rows[i].want_exit) { fprintf(stderr, "accept[%s]: exit %d, want %d\n", accept_rows[i].label, got, accept_rows[i].want_exit); fail++; } char cs_s[128], ws_s[128]; snprintf(cs_s, sizeof cs_s, "%s/wwan_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwan_%d_%d_ww.s", tmpdir, getpid(), i); snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src); if (runwait(cmd) != 0) { fprintf(stderr, "accept[%s]: w6c failed\n", accept_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, "accept[%s]: w6c_ww failed\n", accept_rows[i].label); fail++; runwait(rmcmd); continue; } if (slurp_eq(cs_s, ws_s) != 0) { fprintf(stderr, "accept[%s]: cs/ww .s DIFFER (rule-10)\n", accept_rows[i].label); fail++; } runwait(rmcmd); } /* Reject rows: BOTH stages must fail to build (loud reject). */ for (int i = 0; reject_rows[i].src; i++, n++) { char src[64]; snprintf(src, sizeof src, "/tmp/wwrn_%d_%d.ww", getpid(), i); FILE *f = fopen(src, "wb"); if (f == NULL) { fail++; continue; } fputs(reject_rows[i].src, f); fclose(f); char cmd[2048], dst[80]; snprintf(dst, sizeof dst, "/tmp/wwrn_%d_%d.s", getpid(), i); snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, dst, src); int cs_rc = runwait(cmd); snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c_ww, dst, src); int ww_rc = runwait(cmd); if (cs_rc == 0) { fprintf(stderr, "reject[%s]: cstage ACCEPTED (want reject)\n", reject_rows[i].label); fail++; } if (ww_rc == 0) { fprintf(stderr, "reject[%s]: wwstage ACCEPTED (want reject)\n", reject_rows[i].label); fail++; } unlink(src); unlink(dst); } if (fail) { fprintf(stderr, "%d/%d arrlit-elem-narrow tests failed\n", fail, n); return 1; } printf("arrnarrow: %d/%d ok (accept: run + cs==ww; reject: both-stage fail)\n", n, n); return 0; }