From 6f1176346233fc183ea25244b10a9a725f881fb4 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Fri, 12 Jun 2026 11:11:33 +0900 Subject: [PATCH] wcc/ww: reject non-constant array dimension (tinfofornode N_TARRAY) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- selfhost/cmd/w6c/main.combined.ww | 27 ++++++++++++++++++++++++++- selfhost/cmd/wcc/check.ww | 27 ++++++++++++++++++++++++++- selfhost/cmd/wwdump/main.combined.ww | 27 ++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 8279700c..97d22e5b 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -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; }; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 7996901f..8c816a8e 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -2014,7 +2014,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; }; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 98bf1f78..2f0855b6 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -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; };