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).
This commit is contained in:
2026-05-27 12:21:15 +09:00
parent 88f3d67b28
commit cbeffea7d8
7 changed files with 508 additions and 1 deletions

View File

@@ -62,10 +62,21 @@
* 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).
* - Nested arrays `[N][M]T` — no current consumer.
* - `...` 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).
@@ -140,6 +151,59 @@ static const struct row rows[] = {
"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 }
};
@@ -250,6 +314,38 @@ main(void)
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;