Files
ww/test/wcc/919_array_static_init_run.c
Hojun-Cho cbeffea7d8 wcc: 2D array [N][M]T static-init + double-index read (#156, A.3 capstone)
Close A.3's deferred shape-14 (nested array). (a) emit_array_lit_bytes
gains a TY_ARRAY-element arm (mechanical clone of the TY_STRUCT-element
arm — recurses; esz=etype->size, rule-13; ...-nested loud-reject). (b)
double-index read tbl[i][j]: when the indexed element is TY_ARRAY, leave
the sub-array ADDRESS in AX instead of dereferencing (sister of #135's
N_DOT-base fix, on the N_INDEX path) — new elemisarrayc/tinfoisarray
helpers, both stages. Storage + read = one 2D-end-to-end concern (A.2/A.3
storage+LOAD precedent).

Unblocks strconv fold-4's powers_of_ten[596][2]u64 (direct double-index
access). Bootstrap-NEUTRAL (new arms gate on TY_ARRAY-element; 1D
consumers byte-identical, 990-997 green). Test 919 +2D rows + 3D +
...-nested-reject. Deferred siblings: #155 (sub-array bind / whole-
aggregate copy), #160 (global-struct-field index base).
2026-05-27 12:21:15 +09:00

357 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
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 src[64];
snprintf(src, sizeof src, "/tmp/wwari_%d_%d.ww", getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwari_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
char cmd[2048];
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s",
tmpdir, bin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
unlink(src); rmdir(tmpdir);
continue;
}
char outbin[128];
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
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++;
}
unlink(outbin); rmdir(tmpdir);
char cs_s[64], ws_s[64];
snprintf(cs_s, sizeof cs_s, "/tmp/wwari_%d_%d_cs.s",
getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwari_%d_%d_ww.s",
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++; unlink(src); 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++; unlink(src); unlink(cs_s); 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++;
}
unlink(src); unlink(cs_s); unlink(ws_s);
}
/* #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;
}