wcc/check: #141 def-dim array as struct field — fold def in dim, shared arrayelen across 3 ww readers (both stages)

A def-dimensioned array [MAX]u8 used as a struct field was BOTH-WRONG: cstage
loud-rejected ("array length must be an integer literal"); wwstage silently
sized the dim to 0, so the next field overlapped it (frame-smash). The
reference is neither stage — it is Hare: accept + fold the def.

cstage: fold the def into the dim via eval_def_const. The fold needs def NAMES
visible when resolve_typedecl walks struct bodies, so a stub loop binds
def-name stubs (type=NULL, filled in place by the existing def loop) before
resolve_typedecl — this extends check_file's existing names-first USE+TYPEDECL
pass to DEFs; def-TYPE resolution stays in its original order, and the
kind-filtered type lookup (#225) keeps the SK_DEF stub out of type position.

wwstage: one shared arrayelen(c, rhs) (INTLIT -> uval; else evaldefconst;
else 0) routed through astsize / tinfofornode / checkarrlitfits.

Closes #13's def-dim cstage-reject half (the slice-repeat clause stays open).
Pin test/wcc/951 (5 rows incl a cross-module os.PATH_MAX dim + a ~4KB shape;
teeth = cstage loud-reject + ww frame-smash). cgen-first blocker for the
path::buffer arc (type buffer = struct{[MAX]u8, ...}).
This commit is contained in:
2026-06-08 01:00:29 +09:00
parent 620e733444
commit f1dcd4ecae
6 changed files with 382 additions and 44 deletions

View File

@@ -11387,10 +11387,10 @@ fn astsize(c: *checker, t: *node) i64 = {
if (k == nkind.N_TCHAN) { return 8i64; };
if (k == nkind.N_TFN) { return 8i64; };
if (k == nkind.N_TARRAY) {
let elen: i64 = 0i64;
if (t.rhs != nil) {
if (t.rhs.kind == nkind.N_INTLIT) { elen = t.rhs.uval: i64; };
};
// #141: a def-dimensioned field array sized to 0 here, so the
// struct loop (off += astsize(field)) overlapped the next
// field; arrayelen folds the def.
let elen: i64 = arrayelen(c, t.rhs): i64;
return astsize(c, t.lhs) * elen;
};
if (k == nkind.N_TTUPLE) {
@@ -11823,6 +11823,23 @@ fn stampintlit(n: *node, v: u64) void = {
// n.type_ left intact (the type exprtype inferred for the rhs).
};
// arrayelen — an array type's dimension as an element count. An
// N_INTLIT yields its uval; a def-ref or const-expr dim (`[MAX]u8`,
// MAX a def) folds through evaldefconst (#141, ken oracle); a nil
// child (the `[_]T` inferred-length sentinel) or non-const rhs gives
// 0. cstage carries the folded length in the resolved Type, but
// wwstage computes size lazily on independent paths (no AST-stamp
// SSoT), so every N_TARRAY length reader routes here to fold the dim
// identically — astsize (struct layout), tinfofornode (canonical
// tinfo), checkarrlitfits (count gate).
fn arrayelen(c: *checker, rhs: *node) u64 = {
if (rhs == nil) { return 0u64; };
if (rhs.kind == nkind.N_INTLIT) { return rhs.uval; };
let v: u64 = 0u64;
if (evaldefconst(c, rhs, &v, 0)) { return v; };
return 0u64;
};
// enumvalfold — fold an enum member's value expression to a u64
// constant. The Hare-fidelity set: literal leaves, unary +/-/~,
// binary arithmetic (+ - * / %), bitwise (& | ^), shifts (<< >>),
@@ -12159,10 +12176,7 @@ 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 = 0u64;
if (n.rhs != nil) {
if (n.rhs.kind == nkind.N_INTLIT) { elen = n.rhs.uval; };
};
let elen: u64 = arrayelen(c, n.rhs); // #141: fold def-dim
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; };
@@ -14466,12 +14480,13 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// A nil or zero length child stays exempt: nil is the un-inferred
// [_] sentinel in def/struct-field contexts and 0 doubles as both
// [0] and the cstage [_] sentinel (conflation: task #11); the let
// paths stamp the real length before reaching here. A non-INTLIT
// length child (def-named [N]) is also exempt — task #13.
// paths stamp the real length before reaching here. #141: a def-dim
// child (`[MAX]u8`) now folds via arrayelen and is count-checked
// like a literal (closes #13's def-dim exemption).
// Under-long (count < N, no `...`) stays accepted as before; Hare
// rejects it — task #10.
if (arrtn.rhs != nil && arrtn.rhs.kind == nkind.N_INTLIT
&& arrtn.rhs.uval > 0u64) {
let declen: u64 = arrayelen(c, arrtn.rhs);
if (declen > 0u64) {
let cnt: u64 = 0u64;
let ce: *node = rhs.list;
for (ce != nil) {
@@ -14482,11 +14497,11 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
if (!cskip) { cnt += 1u64; };
ce = ce.next;
};
if (cnt > arrtn.rhs.uval) {
if (cnt > declen) {
cerr("array literal has ");
cerr(strconv.u64tos(cnt, strconv.base.DEC));
cerr(" elements but declared array holds ");
cerr(strconv.u64tos(arrtn.rhs.uval, strconv.base.DEC));
cerr(strconv.u64tos(declen, strconv.base.DEC));
cerr("\n");
c.errs += 1;
return;