w6c_ww/cgen: size [N]enum element from tinfo not slotsize (fix #8)

wwstage sized a named-enum array element (`[N]tk`, tk = enum i32) as a
raw 8-byte slot instead of its i32 backing (4), via two sibling code
paths that both derived the element width structurally and missed the
enum's underlying size:

  - elemsizeofc (cgenutil.ww) was the odd-one-out among the elem*c
    helpers: elemissignedc/elemisfloatc already read the checker-stamped
    tinfo (t.type_.sub), but elemsizeofc went elemsizeof->primsize->
    slotsize, and primsize("tk")=0 fell through to 8. This drove the
    cgindex READ: `a[i]` strode by 8 (MOVQ) where cstage strode by 4
    (MOVSXD), reading the wrong/out-of-bounds element for i>=1.

  - the array-literal init STORE (cgenstmt.ww) computed its own esz the
    same way (primsize=0 -> stayed at the 8 sentinel, enum is not an
    aggregate), so a local `[N]enum` literal stored at stride 8 into a
    stride-4 frame slot, overrunning it and smashing the saved BP /
    return addr -> wwstage-built binary SEGFAULTED.

Both align UP to cstage, which reads the stamped element size uniformly
(N_INDEX idx_eff(bt)->sub->size; N_LET array-init lu->sub->size,
cgen.c:6387). The read fix brings all four elem*c helpers onto the same
tinfo SSoT; the store fix takes the stamped element size for a narrow
scalar. Closing both close-by-construction at the size source.

No in-tree [N]enum / aliased-narrow element existed before kwtab, so
this was byte-id-gate-blind until now. test/wcc/682_arr_enum_elem.c
pins it table-driven: global+local reads, local init-store, signed
sign-extend, and a frame-smash row, each run through both stages with
exit-code and cstage==wwstage asm-byte-id checks.
This commit is contained in:
2026-06-03 14:48:12 +09:00
parent 6b67655eca
commit d39691a3d7
6 changed files with 413 additions and 0 deletions

View File

@@ -1773,6 +1773,20 @@ fn cglet(c: *cgen, n: *node) void = {
|| esubti.kind == tykind.TY_ARRAY
|| esubti.kind == tykind.TY_TUPLE);
if (isagg) { esz = esubti.size: i32; };
// #8: a named-narrow element (`[N]tk`, tk = enum i32) is
// neither a builtin prim (primsize=0 above, so esz stayed
// the 8 sentinel) nor an aggregate, so the scalar store kept
// an 8B stride/MOVQ and overran the stride-4 frame slot —
// smashing the saved BP / return addr (SEGFAULT). Mirror
// cstage's uniform lu->sub->size (cgen.c:6387) and the
// elemsizeofc read-side fix: take the stamped element tinfo's
// size for a narrow scalar (1/2/4). Wider non-prim elements
// (tagged/slice/str two-half) stay the documented follow-up
// at :1742-1744 — the single-MOVx store below is scalar-only.
if (!isstrel && !isagg && esz == 8 && esubti != nil) {
let es: i32 = esubti.size: i32;
if (es == 1 || es == 2 || es == 4) { esz = es; };
};
let mop: str = tnodestoreop(c, elemn, esz);
// float element → store FROM X0 (MOVSS/MOVSD): cgexpr
// leaves a float in X0 and for f32 the #104 CVTSD2SS