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

@@ -653,13 +653,19 @@ resolve_type(Checker *c, Node *n)
case N_TSLICE:
return type_slice(c->a, resolve_type(c, n->lhs));
case N_TARRAY: {
u64 len = 0;
u64 len = 0, v;
if (n->rhs == NULL) {
/* `[_]T` — length inferred at the use site (currently
* only `let x: [_]T = arrlit;`). Leave alen=0 as a
* sentinel; clet patches it from the initialiser. */
} else if (n->rhs->kind == N_INTLIT) {
len = n->rhs->uval;
} else if (eval_def_const(c, n->rhs, &v, 0)) {
/* #141: a def-dimensioned `[MAX]u8`; fold the
* const-expr dimension (the same machinery #133's
* let-init fold uses). The err below stays for a
* genuinely non-const rhs. */
len = v;
} else {
err(c, n->pos, "array length must be an integer literal");
}
@@ -2716,6 +2722,29 @@ check_file(Checker *c, Node *file)
}
d->type = named;
}
/* #141: bind def NAMES before resolving type bodies, so a struct
* field `[MAX]u8` whose dimension is a def-ref folds via
* eval_def_const (which reads decl->rhs) when resolve_typedecl
* walks the body below. The def's type is resolved in the
* decl loop further down; only the name->decl binding is needed
* here. A foldable stub carries type NULL until then. A duplicate
* (prev already bound non-USE) is left for that loop to diagnose. */
c->cur_mod = NULL;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_DEF) continue;
c->cur_mod = decl_mod(file, d);
const char *mod = decl_mod(file, d);
Sym *prev = scope_lookup_local(c->cur, d->str);
if (prev && prev->kind == SK_USE) {
prev->kind = SK_DEF; prev->decl = d;
prev->use_alias = 1;
if (mod && prev->mod == NULL) prev->mod = mod;
} else if (prev == NULL) {
scope_define_in_module(c->cur, d->str, mod,
SK_DEF, NULL, d);
}
}
c->cur_mod = NULL;
for (Node *d = file->list; d; d = d->next) {
if (d->kind != N_TYPEDECL) continue;
resolve_typedecl(c, d);
@@ -2733,7 +2762,11 @@ check_file(Checker *c, Node *file)
d->type = t;
Sym *prev = scope_lookup_local(c->cur, d->str);
const char *mod = decl_mod(file, d);
if (prev && prev->kind == SK_USE) {
if (prev && prev->kind == SK_DEF && prev->decl == d) {
/* #141: the foldable stub bound before type-body
* resolution; fill in its now-resolved type. */
prev->type = t;
} else if (prev && prev->kind == SK_USE) {
/* `use mod; ... def mod = ...;` — promote the
* SK_USE to the def symbol but remember it was
* also a module name so dotted qualifiers