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

@@ -12221,6 +12221,43 @@ fn elemisf32c(c: *cgen, t: *node) bool = {
return typeisf32(ti.sub);
};
// elemisarrayc — given an indexable type-AST (`*T` / `[]T` / `[N]T`), is
// its element itself an array (`[N][M]T` → element `[M]T`)? cgindex then
// leaves the sub-array's ADDRESS in the result reg rather than
// dereferencing — a nested index adds its offset and only the final
// scalar element dereferences (#156, sister of #135 N_DOT-base-on-
// array-field). Node-based with the `*[N]T` drill-through, mirroring
// elemsizeof (:920-948) so elem-is-array aligns with the esz this same
// tnode feeds. cstage twin: idx_eff(bt)->sub unwrapped == TY_ARRAY
// (cmd/w6c/cgen.c). `c` kept for signature symmetry with elemisfloatc.
fn elemisarrayc(c: *cgen, t: *node) bool = {
if (t == nil) { return false; };
let k: nkind = t.kind;
let elem: *node = nil;
if (k == nkind.N_TPTR) { elem = t.lhs; };
if (k == nkind.N_TSLICE) { elem = t.lhs; };
if (k == nkind.N_TARRAY) { elem = t.lhs; };
if (elem == nil) { return false; };
if (k == nkind.N_TPTR) {
if (elem.kind == nkind.N_TARRAY) {
if (elem.lhs != nil) { elem = elem.lhs; };
};
};
return elem.kind == nkind.N_TARRAY;
};
// tinfoisarray — TY_ARRAY (NAMED-aware), the tinfo-keyed companion to
// elemisarrayc for cgindex's N_DOT/N_INDEX base branches, where the
// element type comes from n.type_ (stamped tinfo) not a tnode. Same
// role as typeisslice/typeisstr in lib/ww/typ.ww; kept cgen-local to
// avoid widening the frontend surface for one #156 read-half check.
fn tinfoisarray(t: *tinfo) bool = {
let u: *tinfo = t;
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
if (u == nil) { return false; };
return u.kind == tykind.TY_ARRAY;
};
// fieldissignedc — does this field/element type-AST need sign-
// extension on a sub-word load? One-liner via typeissigned (cstage
// cgen.c:240 `fld_issigned` SSoT). t.type_ is stamped at check.ww
@@ -12345,6 +12382,12 @@ fn elemsizeof(t: *node) i32 = {
if (elem == nil) { return 1; };
// `*[N]T`: drill through the pointer into the array's element so
// indexing scales by T's width, not the whole-array byte size.
// FOOTGUN (#156): this drill ALSO fires for a bare 2D `[N][M]T`
// (elem = the inner `[M]T`), so elemsizeof of a 2D array bottoms
// out at the SCALAR T size, NOT the `[M]T` sub-array stride. 2D
// double-index (cgindex) needs the sub-array stride — call
// elemsizeofc, the 2D-correct entry, which recovers slotsize([M]T)
// when elemsizeof returns 8. Never call elemsizeof for a 2D stride.
if (elem.kind == nkind.N_TARRAY) {
if (elem.lhs != nil) { elem = elem.lhs; };
};
@@ -15553,6 +15596,14 @@ fn cgindex(c: *cgen, n: *node) void = {
// from the SAME tinfo esz reads — never a fresh node-stamp (#121).
let float_elem: bool = false;
let f32_elem: bool = false;
// #156 (PREREQ-1 read-half): element is itself an array ([N][M]T →
// element [M]T) → leave the sub-array's ADDRESS in the result reg
// instead of dereferencing; the outer index adds its offset and the
// final scalar element dereferences. Sister of #135. Mirrors cstage
// esubu->kind == TY_ARRAY. Node-based (elemisarrayc) for ident bases,
// n.type_ tinfo-based for N_DOT/N_INDEX bases — same source split as
// esz above.
let elem_isarray: bool = false;
// #1/Phase 3: str and slice are both 24B (and a >16B struct is
// 24B+ too), so the header branches below MUST gate on KIND
// (elemisstr/elemisslice, mirroring cstage's elem_is_str||
@@ -15580,6 +15631,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, baselocal.tnode);
float_elem = elemisfloatc(c, baselocal.tnode);
f32_elem = elemisf32c(c, baselocal.tnode);
elem_isarray = elemisarrayc(c, baselocal.tnode);
} else {
let tn: *node = letvartnode(c, bn);
// #129 A.3: array-typed defs now have DATA storage;
@@ -15596,6 +15648,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, tn);
float_elem = elemisfloatc(c, tn);
f32_elem = elemisf32c(c, tn);
elem_isarray = elemisarrayc(c, tn);
};
if (tn.kind == nkind.N_TPTR) {
isglobalptr = true;
@@ -15604,6 +15657,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, tn);
float_elem = elemisfloatc(c, tn);
f32_elem = elemisf32c(c, tn);
elem_isarray = elemisarrayc(c, tn);
};
};
};
@@ -15616,6 +15670,7 @@ fn cgindex(c: *cgen, n: *node) void = {
// stays unset, as before.
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
} else { if (base.kind == nkind.N_INDEX) {
// #60: chained `names[i][k]` — n.type_ is the checker-
// stamped outer element tinfo (indexresult over the inner
@@ -15628,6 +15683,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = typeissigned(et);
float_elem = typeisfloat(et);
f32_elem = typeisf32(et);
elem_isarray = tinfoisarray(et);
};
};};};
};
@@ -15688,6 +15744,12 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("(SB), BX\n");
};
emitline("\tADDQ\tAX, BX\n");
// #156: array element ([N][M]T) → leave the sub-array ADDRESS
// in AX (BX holds base+idx*esz); nested index dereferences.
if (elem_isarray) {
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (elem_tagged) {
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
@@ -15741,6 +15803,12 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("(BP), BX\n");
};
emitline("\tADDQ\tAX, BX\n");
// #156: array element ([N][M]T) → leave the sub-array ADDRESS
// in AX (BX holds base+idx*esz); nested index dereferences.
if (elem_isarray) {
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (elem_tagged) {
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
@@ -15785,6 +15853,11 @@ fn cgindex(c: *cgen, n: *node) void = {
};
emitline("\tPOPQ\tBX\n");
emitline("\tADDQ\tBX, AX\n");
// #156: array element ([N][M]T) → AX already holds &elem
// (base+idx*esz); a nested index dereferences. See ident arms.
if (elem_isarray) {
return;
};
if (elem_tagged) {
// AX holds the element address. Copy to BX (loading slot+0
// into AX clobbers it), then read slot words.
@@ -25498,6 +25571,51 @@ fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
return true;
};
// #129 A.3 capstone (PREREQ-1, #156): nested-array element [M]T
// inside [N][M]T. Mirror of the TY_STRUCT-element arm above and of
// the TY_ARRAY-field-in-struct arm in emitstructlitbytes — recurse
// into emitarraylitbytes per element; recursion bottoms out at
// scalar (int/float) elements. esz = au.sub.size gives the per-
// element stride (rule 13). The `...` repeat marker with nested-
// array elements is rejected loud (rule 7): no consumer needs it
// (powers_of_ten is fully enumerated).
if (eu != nil && eu.kind == tykind.TY_ARRAY) {
let idx: i32 = 0;
let e: *node = rhs.list;
for (e != nil && idx < alen) {
if (e.kind == nkind.N_FIELD) {
if (streq(e.str, "...")) {
let m: str = "emitarraylitbytes: '...' repeat with nested-array elements unsupported (#129 A.3, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
if (ev == nil) { return false; };
if (ev.kind != nkind.N_ARRLIT) { return false; };
if (!emitarraylitbytes(c, au.sub, ev, 0)) { return false; };
idx += 1;
e = e.next;
};
if (emit_phase == 0) { return true; };
idx = 0;
e = rhs.list;
for (e != nil && idx < alen) {
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
emitarraylitbytes(c, au.sub, ev, 1);
idx += 1;
e = e.next;
};
for (idx < alen) {
let bb: i32 = 0;
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
idx += 1;
};
return true;
};
if (typeisfloat(au.sub)) {
let isf32: bool = typeisf32(au.sub);
// Validate.

View File

@@ -1567,6 +1567,51 @@ fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
return true;
};
// #129 A.3 capstone (PREREQ-1, #156): nested-array element [M]T
// inside [N][M]T. Mirror of the TY_STRUCT-element arm above and of
// the TY_ARRAY-field-in-struct arm in emitstructlitbytes — recurse
// into emitarraylitbytes per element; recursion bottoms out at
// scalar (int/float) elements. esz = au.sub.size gives the per-
// element stride (rule 13). The `...` repeat marker with nested-
// array elements is rejected loud (rule 7): no consumer needs it
// (powers_of_ten is fully enumerated).
if (eu != nil && eu.kind == tykind.TY_ARRAY) {
let idx: i32 = 0;
let e: *node = rhs.list;
for (e != nil && idx < alen) {
if (e.kind == nkind.N_FIELD) {
if (streq(e.str, "...")) {
let m: str = "emitarraylitbytes: '...' repeat with nested-array elements unsupported (#129 A.3, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
if (ev == nil) { return false; };
if (ev.kind != nkind.N_ARRLIT) { return false; };
if (!emitarraylitbytes(c, au.sub, ev, 0)) { return false; };
idx += 1;
e = e.next;
};
if (emit_phase == 0) { return true; };
idx = 0;
e = rhs.list;
for (e != nil && idx < alen) {
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
emitarraylitbytes(c, au.sub, ev, 1);
idx += 1;
e = e.next;
};
for (idx < alen) {
let bb: i32 = 0;
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
idx += 1;
};
return true;
};
if (typeisfloat(au.sub)) {
let isf32: bool = typeisf32(au.sub);
// Validate.

View File

@@ -867,6 +867,14 @@ fn cgindex(c: *cgen, n: *node) void = {
// from the SAME tinfo esz reads — never a fresh node-stamp (#121).
let float_elem: bool = false;
let f32_elem: bool = false;
// #156 (PREREQ-1 read-half): element is itself an array ([N][M]T →
// element [M]T) → leave the sub-array's ADDRESS in the result reg
// instead of dereferencing; the outer index adds its offset and the
// final scalar element dereferences. Sister of #135. Mirrors cstage
// esubu->kind == TY_ARRAY. Node-based (elemisarrayc) for ident bases,
// n.type_ tinfo-based for N_DOT/N_INDEX bases — same source split as
// esz above.
let elem_isarray: bool = false;
// #1/Phase 3: str and slice are both 24B (and a >16B struct is
// 24B+ too), so the header branches below MUST gate on KIND
// (elemisstr/elemisslice, mirroring cstage's elem_is_str||
@@ -894,6 +902,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, baselocal.tnode);
float_elem = elemisfloatc(c, baselocal.tnode);
f32_elem = elemisf32c(c, baselocal.tnode);
elem_isarray = elemisarrayc(c, baselocal.tnode);
} else {
let tn: *node = letvartnode(c, bn);
// #129 A.3: array-typed defs now have DATA storage;
@@ -910,6 +919,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, tn);
float_elem = elemisfloatc(c, tn);
f32_elem = elemisf32c(c, tn);
elem_isarray = elemisarrayc(c, tn);
};
if (tn.kind == nkind.N_TPTR) {
isglobalptr = true;
@@ -918,6 +928,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, tn);
float_elem = elemisfloatc(c, tn);
f32_elem = elemisf32c(c, tn);
elem_isarray = elemisarrayc(c, tn);
};
};
};
@@ -930,6 +941,7 @@ fn cgindex(c: *cgen, n: *node) void = {
// stays unset, as before.
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
} else { if (base.kind == nkind.N_INDEX) {
// #60: chained `names[i][k]` — n.type_ is the checker-
// stamped outer element tinfo (indexresult over the inner
@@ -942,6 +954,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = typeissigned(et);
float_elem = typeisfloat(et);
f32_elem = typeisf32(et);
elem_isarray = tinfoisarray(et);
};
};};};
};
@@ -1002,6 +1015,12 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("(SB), BX\n");
};
emitline("\tADDQ\tAX, BX\n");
// #156: array element ([N][M]T) → leave the sub-array ADDRESS
// in AX (BX holds base+idx*esz); nested index dereferences.
if (elem_isarray) {
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (elem_tagged) {
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
@@ -1055,6 +1074,12 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("(BP), BX\n");
};
emitline("\tADDQ\tAX, BX\n");
// #156: array element ([N][M]T) → leave the sub-array ADDRESS
// in AX (BX holds base+idx*esz); nested index dereferences.
if (elem_isarray) {
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (elem_tagged) {
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
@@ -1099,6 +1124,11 @@ fn cgindex(c: *cgen, n: *node) void = {
};
emitline("\tPOPQ\tBX\n");
emitline("\tADDQ\tBX, AX\n");
// #156: array element ([N][M]T) → AX already holds &elem
// (base+idx*esz); a nested index dereferences. See ident arms.
if (elem_isarray) {
return;
};
if (elem_tagged) {
// AX holds the element address. Copy to BX (loading slot+0
// into AX clobbers it), then read slot words.

View File

@@ -819,6 +819,43 @@ fn elemisf32c(c: *cgen, t: *node) bool = {
return typeisf32(ti.sub);
};
// elemisarrayc — given an indexable type-AST (`*T` / `[]T` / `[N]T`), is
// its element itself an array (`[N][M]T` → element `[M]T`)? cgindex then
// leaves the sub-array's ADDRESS in the result reg rather than
// dereferencing — a nested index adds its offset and only the final
// scalar element dereferences (#156, sister of #135 N_DOT-base-on-
// array-field). Node-based with the `*[N]T` drill-through, mirroring
// elemsizeof (:920-948) so elem-is-array aligns with the esz this same
// tnode feeds. cstage twin: idx_eff(bt)->sub unwrapped == TY_ARRAY
// (cmd/w6c/cgen.c). `c` kept for signature symmetry with elemisfloatc.
fn elemisarrayc(c: *cgen, t: *node) bool = {
if (t == nil) { return false; };
let k: nkind = t.kind;
let elem: *node = nil;
if (k == nkind.N_TPTR) { elem = t.lhs; };
if (k == nkind.N_TSLICE) { elem = t.lhs; };
if (k == nkind.N_TARRAY) { elem = t.lhs; };
if (elem == nil) { return false; };
if (k == nkind.N_TPTR) {
if (elem.kind == nkind.N_TARRAY) {
if (elem.lhs != nil) { elem = elem.lhs; };
};
};
return elem.kind == nkind.N_TARRAY;
};
// tinfoisarray — TY_ARRAY (NAMED-aware), the tinfo-keyed companion to
// elemisarrayc for cgindex's N_DOT/N_INDEX base branches, where the
// element type comes from n.type_ (stamped tinfo) not a tnode. Same
// role as typeisslice/typeisstr in lib/ww/typ.ww; kept cgen-local to
// avoid widening the frontend surface for one #156 read-half check.
fn tinfoisarray(t: *tinfo) bool = {
let u: *tinfo = t;
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
if (u == nil) { return false; };
return u.kind == tykind.TY_ARRAY;
};
// fieldissignedc — does this field/element type-AST need sign-
// extension on a sub-word load? One-liner via typeissigned (cstage
// cgen.c:240 `fld_issigned` SSoT). t.type_ is stamped at check.ww
@@ -943,6 +980,12 @@ fn elemsizeof(t: *node) i32 = {
if (elem == nil) { return 1; };
// `*[N]T`: drill through the pointer into the array's element so
// indexing scales by T's width, not the whole-array byte size.
// FOOTGUN (#156): this drill ALSO fires for a bare 2D `[N][M]T`
// (elem = the inner `[M]T`), so elemsizeof of a 2D array bottoms
// out at the SCALAR T size, NOT the `[M]T` sub-array stride. 2D
// double-index (cgindex) needs the sub-array stride — call
// elemsizeofc, the 2D-correct entry, which recovers slotsize([M]T)
// when elemsizeof returns 8. Never call elemsizeof for a 2D stride.
if (elem.kind == nkind.N_TARRAY) {
if (elem.lhs != nil) { elem = elem.lhs; };
};

View File

@@ -12221,6 +12221,43 @@ fn elemisf32c(c: *cgen, t: *node) bool = {
return typeisf32(ti.sub);
};
// elemisarrayc — given an indexable type-AST (`*T` / `[]T` / `[N]T`), is
// its element itself an array (`[N][M]T` → element `[M]T`)? cgindex then
// leaves the sub-array's ADDRESS in the result reg rather than
// dereferencing — a nested index adds its offset and only the final
// scalar element dereferences (#156, sister of #135 N_DOT-base-on-
// array-field). Node-based with the `*[N]T` drill-through, mirroring
// elemsizeof (:920-948) so elem-is-array aligns with the esz this same
// tnode feeds. cstage twin: idx_eff(bt)->sub unwrapped == TY_ARRAY
// (cmd/w6c/cgen.c). `c` kept for signature symmetry with elemisfloatc.
fn elemisarrayc(c: *cgen, t: *node) bool = {
if (t == nil) { return false; };
let k: nkind = t.kind;
let elem: *node = nil;
if (k == nkind.N_TPTR) { elem = t.lhs; };
if (k == nkind.N_TSLICE) { elem = t.lhs; };
if (k == nkind.N_TARRAY) { elem = t.lhs; };
if (elem == nil) { return false; };
if (k == nkind.N_TPTR) {
if (elem.kind == nkind.N_TARRAY) {
if (elem.lhs != nil) { elem = elem.lhs; };
};
};
return elem.kind == nkind.N_TARRAY;
};
// tinfoisarray — TY_ARRAY (NAMED-aware), the tinfo-keyed companion to
// elemisarrayc for cgindex's N_DOT/N_INDEX base branches, where the
// element type comes from n.type_ (stamped tinfo) not a tnode. Same
// role as typeisslice/typeisstr in lib/ww/typ.ww; kept cgen-local to
// avoid widening the frontend surface for one #156 read-half check.
fn tinfoisarray(t: *tinfo) bool = {
let u: *tinfo = t;
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
if (u == nil) { return false; };
return u.kind == tykind.TY_ARRAY;
};
// fieldissignedc — does this field/element type-AST need sign-
// extension on a sub-word load? One-liner via typeissigned (cstage
// cgen.c:240 `fld_issigned` SSoT). t.type_ is stamped at check.ww
@@ -12345,6 +12382,12 @@ fn elemsizeof(t: *node) i32 = {
if (elem == nil) { return 1; };
// `*[N]T`: drill through the pointer into the array's element so
// indexing scales by T's width, not the whole-array byte size.
// FOOTGUN (#156): this drill ALSO fires for a bare 2D `[N][M]T`
// (elem = the inner `[M]T`), so elemsizeof of a 2D array bottoms
// out at the SCALAR T size, NOT the `[M]T` sub-array stride. 2D
// double-index (cgindex) needs the sub-array stride — call
// elemsizeofc, the 2D-correct entry, which recovers slotsize([M]T)
// when elemsizeof returns 8. Never call elemsizeof for a 2D stride.
if (elem.kind == nkind.N_TARRAY) {
if (elem.lhs != nil) { elem = elem.lhs; };
};
@@ -15553,6 +15596,14 @@ fn cgindex(c: *cgen, n: *node) void = {
// from the SAME tinfo esz reads — never a fresh node-stamp (#121).
let float_elem: bool = false;
let f32_elem: bool = false;
// #156 (PREREQ-1 read-half): element is itself an array ([N][M]T →
// element [M]T) → leave the sub-array's ADDRESS in the result reg
// instead of dereferencing; the outer index adds its offset and the
// final scalar element dereferences. Sister of #135. Mirrors cstage
// esubu->kind == TY_ARRAY. Node-based (elemisarrayc) for ident bases,
// n.type_ tinfo-based for N_DOT/N_INDEX bases — same source split as
// esz above.
let elem_isarray: bool = false;
// #1/Phase 3: str and slice are both 24B (and a >16B struct is
// 24B+ too), so the header branches below MUST gate on KIND
// (elemisstr/elemisslice, mirroring cstage's elem_is_str||
@@ -15580,6 +15631,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, baselocal.tnode);
float_elem = elemisfloatc(c, baselocal.tnode);
f32_elem = elemisf32c(c, baselocal.tnode);
elem_isarray = elemisarrayc(c, baselocal.tnode);
} else {
let tn: *node = letvartnode(c, bn);
// #129 A.3: array-typed defs now have DATA storage;
@@ -15596,6 +15648,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, tn);
float_elem = elemisfloatc(c, tn);
f32_elem = elemisf32c(c, tn);
elem_isarray = elemisarrayc(c, tn);
};
if (tn.kind == nkind.N_TPTR) {
isglobalptr = true;
@@ -15604,6 +15657,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = elemissignedc(c, tn);
float_elem = elemisfloatc(c, tn);
f32_elem = elemisf32c(c, tn);
elem_isarray = elemisarrayc(c, tn);
};
};
};
@@ -15616,6 +15670,7 @@ fn cgindex(c: *cgen, n: *node) void = {
// stays unset, as before.
let dt: *tinfo = n.type_: *tinfo;
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); elemisslice = typeisslice(dt); float_elem = typeisfloat(dt); f32_elem = typeisf32(dt); };
elem_isarray = tinfoisarray(dt);
} else { if (base.kind == nkind.N_INDEX) {
// #60: chained `names[i][k]` — n.type_ is the checker-
// stamped outer element tinfo (indexresult over the inner
@@ -15628,6 +15683,7 @@ fn cgindex(c: *cgen, n: *node) void = {
signed_elem = typeissigned(et);
float_elem = typeisfloat(et);
f32_elem = typeisf32(et);
elem_isarray = tinfoisarray(et);
};
};};};
};
@@ -15688,6 +15744,12 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("(SB), BX\n");
};
emitline("\tADDQ\tAX, BX\n");
// #156: array element ([N][M]T) → leave the sub-array ADDRESS
// in AX (BX holds base+idx*esz); nested index dereferences.
if (elem_isarray) {
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (elem_tagged) {
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
@@ -15741,6 +15803,12 @@ fn cgindex(c: *cgen, n: *node) void = {
emitline("(BP), BX\n");
};
emitline("\tADDQ\tAX, BX\n");
// #156: array element ([N][M]T) → leave the sub-array ADDRESS
// in AX (BX holds base+idx*esz); nested index dereferences.
if (elem_isarray) {
emitline("\tMOVQ\tBX, AX\n");
return;
};
if (elem_tagged) {
if (elem_slot_sz > 24) {
emitline("\tMOVQ\t24(BX), R8\n");
@@ -15785,6 +15853,11 @@ fn cgindex(c: *cgen, n: *node) void = {
};
emitline("\tPOPQ\tBX\n");
emitline("\tADDQ\tBX, AX\n");
// #156: array element ([N][M]T) → AX already holds &elem
// (base+idx*esz); a nested index dereferences. See ident arms.
if (elem_isarray) {
return;
};
if (elem_tagged) {
// AX holds the element address. Copy to BX (loading slot+0
// into AX clobbers it), then read slot words.
@@ -25498,6 +25571,51 @@ fn emitarraylitbytes(c: *cgen, arrt: *tinfo, rhs: *node,
return true;
};
// #129 A.3 capstone (PREREQ-1, #156): nested-array element [M]T
// inside [N][M]T. Mirror of the TY_STRUCT-element arm above and of
// the TY_ARRAY-field-in-struct arm in emitstructlitbytes — recurse
// into emitarraylitbytes per element; recursion bottoms out at
// scalar (int/float) elements. esz = au.sub.size gives the per-
// element stride (rule 13). The `...` repeat marker with nested-
// array elements is rejected loud (rule 7): no consumer needs it
// (powers_of_ten is fully enumerated).
if (eu != nil && eu.kind == tykind.TY_ARRAY) {
let idx: i32 = 0;
let e: *node = rhs.list;
for (e != nil && idx < alen) {
if (e.kind == nkind.N_FIELD) {
if (streq(e.str, "...")) {
let m: str = "emitarraylitbytes: '...' repeat with nested-array elements unsupported (#129 A.3, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
if (ev == nil) { return false; };
if (ev.kind != nkind.N_ARRLIT) { return false; };
if (!emitarraylitbytes(c, au.sub, ev, 0)) { return false; };
idx += 1;
e = e.next;
};
if (emit_phase == 0) { return true; };
idx = 0;
e = rhs.list;
for (e != nil && idx < alen) {
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) { ev = ev.lhs; };
emitarraylitbytes(c, au.sub, ev, 1);
idx += 1;
e = e.next;
};
for (idx < alen) {
let bb: i32 = 0;
for (bb < esz) { emitdatawbyte(0u8); bb += 1; };
idx += 1;
};
return true;
};
if (typeisfloat(au.sub)) {
let isf32: bool = typeisf32(au.sub);
// Validate.