/* * 919_array_static_init_run — runtime + byte-id net for #129 Phase * A.3: module-level let/def with array initializer. * * Pre-A.3 state: * - Int-element arrays (u8/i8/u16/u32/u64/i32 etc) already worked * in both stages via fold_int_literal. * - Float-element arrays ([N]f64, [N]f32) → undef-ref at link * (emit_lets array arm fold_int_literal fails on FLOATLIT). * - Array DEFs (def A: [N]T = [...]) → emit_defs no array arm * (storage missing) AND cgindex broken (reads LEAQ (BP), BX — * stack frame, not data section). * - Array-in-struct field (`def D: dt = dt{tag=42, buf=[...]}`) → * #129 A.2 rule-7 fatal "array field rhs not foldable" — the * shape parked in A.2 awaiting A.3. * * Phase A.3 fix (mirror A.1/A.2 SSoT-helper precedent): * - cstage: emit_array_data + emit_array_lit_bytes helpers with * element-kind dispatch (int via fold_int_literal preserving * bootstrap byte-id, float via inline bitcast + sign-XOR byte- * loop mirror of A.1, struct via emit_struct_lit_bytes recursion * mirror of A.2). Two-pass validate-then-emit avoids partial-byte * corruption on rhs-fold-failure. * - cstage: emit_lets array arm routes through helper; emit_defs * gains array arm. * - cstage: DefArray registry + def_isarraydef populated in * let_collect; cgindex N_INDEX direct-ident `isglobal` gate * widened to (let_islet || def_isarraydef). * - cstage: emit_struct_lit_bytes (A.2 helper) gains TY_ARRAY field * arm calling emit_array_lit_bytes recursively (closes A.2 parked * shape 15). * - wwstage: parallel emitarraydata + emitarraylitbytes; emitstruct * litbytes TY_ARRAY arm; defvartnode helper; cgindex falls through * to defvartnode after letvartnode nil. * * Bootstrap RISK: live consumers in lib/os, lib/bufio, lib/strings, * lib/encoding/utf8 (dfa + masks), lib/strconv/stof_data * (left_shift_table). All use typed-int-literal elements; the int-elem * helper path is byte-for-byte preserved → bootstrap NEUTRAL. * * Rows (size strata 1B/2B/4B/8B × count strata 1/2/4 × int/float/struct/ * empty/no-rhs/def-variant, avoiding the 16B-evade pattern from A.2): * * - (a) `let A: [4]u8 = [1u8, 2u8, 3u8, 4u8]` — 1B regression * - (b) `let A: [4]u32 = [1u32, 2u32, 3u32, 4u32]` — 4B regression * - (c) `let A: [2]u64 = [1u64, 2u64]` — 8B regression * - (d) `let A: [4]i32 = [-1, -2, -3, -4]` — N_UN peel regression * - (e) `let A: [4]f64 = [1.5, -2.5, 3.5, -4.5]` — NEW float-elem * - (f) `let A: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32]` — NEW * f32 narrow + sign-XOR per element * - (g) `def A: [4]u32 = [11u32, 22u32, 33u32, 44u32]` — NEW * def-storage + LOAD-widening * - (h) `def A: [4]f64 = [1.5, 2.5, 3.5, 4.5]` — NEW def-variant of * float * - (i) `def D: dt = dt{tag=42, buf=[1u8,2u8,3u8,4u8]}` — NEW * closes A.2 shape 15 (struct-with-array-field) * - (j) `let A: [4]u8 = [0u8, 0u8, 0u8, 0u8]` — explicit-zero * regression * - (k) `let A: [1]u8 = [0u8]` — single-elem (matches lib/os/ * emptypath pattern) * * Each row: cstage `ww build` + run asserting exit code + w6c vs * w6c_ww `.s` cmp (rule-10 byte-id). * * #156 (PREREQ-1) extends this with 2D `[N][M]T` static-init (a) + * double-index read (b) — the A.3 shape-14 capstone, consumer-driven by * fold-4's powers_of_ten[596][2]u64. emit_array_lit_bytes gains a * TY_ARRAY-element arm (recurse; esz=etype->size); cgindex leaves the * sub-array ADDRESS for an array element (sister of #135). Rows * let_2d_x, def_2d_u64 and let_struct_2d_field + the nested-`...` * loud-reject (rule-7) below. The D.m[i][j] global-struct-field-array * READ stays a * pre-existing gap (#160, 1D+2D, cs≠ww) out of scope here. * * Deferred: * - Pointer-element arrays `[N]*T = [&G, &H]` — needs DATAR per * element (own task/fold). * - `...` repeat with a nested-array element — loud-reject (#156 * rule-7); no consumer (powers_of_ten is fully enumerated). * - Bare-int `[N]u8 = [1, 2, 3, 4]` — #130 (checker issue). * - Partial init `[4]u8 = [1u8]` — checker rejects (parser/checker * decision). */ #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_exit; }; static const struct row rows[] = { { "let_u8_arr", "package main;\n" "let A: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n" "export fn main() i32 = { return A[0]: i32; };\n", 1 }, { "let_u32_arr", "package main;\n" "let A: [4]u32 = [1u32, 2u32, 3u32, 4u32];\n" "export fn main() i32 = { return A[0]: i32; };\n", 1 }, { "let_u64_arr", "package main;\n" "let A: [2]u64 = [1u64, 2u64];\n" "export fn main() i32 = { return A[0]: i32; };\n", 1 }, { "let_i32_neg_arr", "package main;\n" "let A: [4]i32 = [-1, -2, -3, -4];\n" "export fn main() i32 = { return A[0]; };\n", 255 /* -1 */ }, /* Float-element array — NEW in A.3. Includes both signs to exercise * the sign-XOR byte-loop per element. */ { "let_f64_arr", "package main;\n" "let A: [4]f64 = [1.5, -2.5, 3.5, -4.5];\n" "export fn main() i32 = { return (A[0]: i32); };\n", 1 }, { "let_f32_arr", "package main;\n" "let A: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32];\n" "export fn main() i32 = { return (A[0]: i32); };\n", 1 }, /* def-variant exercises the LOAD-widening (def_isarraydef) at * cgindex/cgident. Storage emit also new. */ { "def_u32_arr", "package main;\n" "def A: [4]u32 = [11u32, 22u32, 33u32, 44u32];\n" "export fn main() i32 = { return A[0]: i32; };\n", 11 }, { "def_f64_arr", "package main;\n" "def A: [4]f64 = [1.5, 2.5, 3.5, 4.5];\n" "export fn main() i32 = { return (A[0]: i32); };\n", 1 }, /* The A.2-parked shape-15 — closes via emit_struct_lit_bytes * TY_ARRAY field arm. */ { "let_struct_with_arr_field", "package main;\n" "type dt = struct { tag: i32, buf: [4]u8 };\n" "let D: dt = dt{tag=42, buf=[1u8, 2u8, 3u8, 4u8]};\n" "export fn main() i32 = { return D.tag; };\n", 42 }, { "let_u8_zero_arr", "package main;\n" "let A: [4]u8 = [0u8, 0u8, 0u8, 0u8];\n" "export fn main() i32 = { return A[0]: i32; };\n", 0 }, { "let_u8_single", "package main;\n" "let A: [1]u8 = [7u8];\n" "export fn main() i32 = { return A[0]: i32; };\n", 7 }, /* #156 (PREREQ-1): 2D [N][M]T static-init (a) + double-index read * (b) — the A.3 shape-14 capstone, consumer-driven by fold-4's * powers_of_ten[596][2]u64. emit_array_lit_bytes recurses on the * TY_ARRAY element (esz=etype->size, rule-13); cgindex leaves the * sub-array ADDRESS (not a value) for an array element so the outer * index dereferences the right cell (sister of #135). */ { "let_2d_u64", "package main;\n" "let A: [3][2]u64 = [[1u64,2u64],[3u64,4u64],[5u64,6u64]];\n" "export fn main() i32 = { return A[1][0]: i32; };\n", 3 }, { "def_2d_u64", "package main;\n" "def A: [3][2]u64 = [[1u64,2u64],[3u64,4u64],[5u64,6u64]];\n" "export fn main() i32 = { return A[2][1]: i32; };\n", 6 }, /* variable-index read — the exact fold-4 access pattern * (powers_of_ten[i][0]/[i][1]). 21 + 30 = 51. */ { "let_2d_varidx", "package main;\n" "let A: [3][2]u64 = [[10u64,11u64],[20u64,21u64],[30u64,31u64]];\n" "fn at(i: i32, j: i32) u64 = { return A[i][j]; };\n" "export fn main() i32 = { return (at(1, 1) + at(2, 0)): i32; };\n", 51 }, /* 8-byte 2D: must NOT hit the sz==8 scalar short-circuit (the * isarr8/N_TARRAY guard, #128 lesson) — routes to the array arm. */ { "let_2d_u32_8byte", "package main;\n" "let A: [2][1]u32 = [[7u32],[9u32]];\n" "export fn main() i32 = { return A[1][0]: i32; };\n", 9 }, /* 2D write to one cell, sum all four — verifies the lvalue address * targets the exact cell with no neighbour clobber. 1+2+99+4=106. */ { "let_2d_write", "package main;\n" "let A: [2][2]u64 = [[1u64,2u64],[3u64,4u64]];\n" "export fn main() i32 = { A[1][0] = 99u64; return " "(A[0][0]+A[0][1]+A[1][0]+A[1][1]): i32; };\n", 106 }, /* struct field that is itself a 2D array — static-init emit + * layout (read D.tag). The D.m[i][j] field-array READ exercises a * pre-existing global-struct-field-base bug (#137/#150 family, * 1D+2D, cs≠ww) out of PREREQ-1 scope — the array bytes are * covered by the cs==ww byte-id gate below. */ { "let_struct_2d_field", "package main;\n" "type dt = struct { tag: i32, m: [2][2]u64 };\n" "let D: dt = dt{tag=42, m=[[1u64,2u64],[3u64,4u64]]};\n" "export fn main() i32 = { return D.tag; };\n", 42 }, /* 3D — locks recursion-depth>2 in both emit (nested TY_ARRAY arm * recurses twice) and read (double-then-single index, two * address-leaves). [[[1,2],[3,4]],[[5,6],[7,8]]]; A[1][1][0] = 7. */ { "let_3d_u8", "package main;\n" "let A: [2][2][2]u8 = " "[[[1u8,2u8],[3u8,4u8]],[[5u8,6u8],[7u8,8u8]]];\n" "export fn main() i32 = { return A[1][1][0]: i32; };\n", 7 }, { NULL, NULL, 0 } }; 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, "arrinit: 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/wwari_%d_d_%d", getpid(), i); mkdir(tmpdir, 0755); char rmcmd[160]; snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); char src[128]; snprintf(src, sizeof src, "%s/wwari_%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 outbin[128]; snprintf(outbin, sizeof outbin, "%s/wwari_%d_%d", tmpdir, getpid(), i); char cmd[2048]; 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++; } char cs_s[128], ws_s[128]; snprintf(cs_s, sizeof cs_s, "%s/wwari_%d_%d_cs.s", tmpdir, getpid(), i); snprintf(ws_s, sizeof ws_s, "%s/wwari_%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, "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); } /* #156 rule-7: a `...` repeat marker with a nested-array element is * a loud reject in BOTH stages (no consumer needs it; powers_of_ten * is fully enumerated). The compile must FAIL, not silently emit * wrong bytes. Separate from the rows table (which asserts build * success). */ { char src[64]; snprintf(src, sizeof src, "/tmp/wwari_%d_rej.ww", getpid()); FILE *f = fopen(src, "wb"); if (f != NULL) { fputs("package main;\n" "let A: [4][2]u64 = [[1u64, 2u64]...];\n" "export fn main() i32 = { return 0; };\n", f); fclose(f); } char cmd[2048]; snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>/dev/null", w6c, src); int rc_cs = runwait(cmd); snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>/dev/null", w6c_ww, src); int rc_ww = runwait(cmd); n++; if (rc_cs == 0 || rc_ww == 0) { fprintf(stderr, "row[nested_ellipsis_reject]: expected " "BOTH stages to reject (cs=%d ww=%d), want nonzero " "(#156 rule-7)\n", rc_cs, rc_ww); fail++; } unlink(src); } if (fail) { fprintf(stderr, "%d/%d array-static-init tests failed\n", fail, n); return 1; } printf("arrinit: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n); return 0; }