wcc/ww: reject non-constant array dimension (tinfofornode N_TARRAY)

arrayelen silently folded a non-const dimension to 0 — the 0-sized
slot aliased its neighbor (review item: cs loud / ww rc=0 clobber).
Mirror cstage check.c:715. Test rows land with the wave's final commit.
This commit is contained in:
2026-06-12 11:11:33 +09:00
parent 9f4626c546
commit 6f11763462
3 changed files with 78 additions and 3 deletions

View File

@@ -12434,7 +12434,32 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// Reverts A.4's r.size override (which conflated stride with
// natural size); the slot-padded stride now lives in slotsize
// where cgenutil's fast-path reads it.
let elen: u64 = arrayelen(c, n.rhs); // #141: fold def-dim
// #38/F2 (review item 2): a non-const, non-`[_]T` dimension is
// LOUD here, mirroring cstage resolve_type N_TARRAY
// (cmd/wcc/check.c:715 "array length must be an integer literal").
// arrayelen folds 0 for both the `[_]T` sentinel AND a runtime
// dim, so the size-walker reader (astsize, via arrayelen) can't
// tell them apart; resolve the type HERE — the one resolve seam
// cstage gates at — so c.errs trips before any 0-sized slot ships.
// The fold is inlined (not via arrayelen) to error exactly once:
// evaldefconst itself reports div-by-zero / unsupported-op, so a
// guard that re-folds would double-report. arrayelen stays the
// fold SSoT for astsize's later size() read.
let elen: u64 = 0u64; // #141: fold def-dim
if (n.rhs != nil) {
if (n.rhs.kind == nkind.N_INTLIT) {
elen = n.rhs.uval;
} else {
let v: u64 = 0u64;
if (evaldefconst(c, n.rhs, &v, 0)) {
elen = v;
} else {
cerr(n.file); cerr(": ");
cerr("error: array length must be an integer literal\n");
c.errs += 1;
};
};
};
let sub: *tinfo = tinfofornode(c, n.lhs);
// #62/#69: `type a = [2]a` value cycle — loud, cstage twin.
if (circularnamed(c, sub, n)) { sub = c.tc.tyerr; };