w6c+wcc/check: infer [_]T array length from initializer element count (fix #7)
`[_]T = [...]` (canonical Hare array-length inference) silently miscompiled to a zero-length array: the parser already left the array type's length child nil as the infer sentinel — distinct from an explicit [N] — but neither checker stamped the real count, so `len(x)` returned 0 with no diagnostic (rule-7 silent miscompile). Module-level was worse on wwstage, where `x.len` on ANY global array (even an explicit [N]) fell to the SB fallback and mis-emitted `MOVQ len(SB), AX` (linker: undefined reference to len). The length lives in the stamped TYPE and cgen already keys stride / length / data-emission off it, so stamping the inferred count at the one checker inference point closes it permanently (rob's #7 ruling): - check.c clet + module-level N_LET pass-2: count the initializer's elements and patch the array type's length (the Sym too, so a later x.len reads the inferred alen). No-init / non-array init can't infer -> loud error, never a silent zero-length array. - check.ww inferarraylen: the wwstage twin — stamp a synthesized N_INTLIT length child before resolvewalk caches the array tinfo; same loud-error rule. Idempotent for the module-level double-call. - cgenexpr.ww cgdot: the missing wwstage arm for a top-level [N]T global's .len / .ptr (cstage cgen.c:8011 already had it). - cgenutil.ww letslotsize: drop the now-redundant [_] slot-size intercept — a workaround for this very bug; the stamped length flows through the general slotsize path (rule 7). Both stages converge byte-identical; new table-driven test 684 covers [_]int/[_]str/[_]u8 local + module-level, len + element read-back, dual-stage runtime + asm byte-id, plus three negative no-infer rows.
This commit is contained in:
@@ -1901,10 +1901,14 @@ clet(Checker *c, Node *n)
|
||||
if (n->rhs) initt = cexpr(c, n->rhs);
|
||||
c->alloc_octx = saved_octx;
|
||||
/* `let xs: [_]T = arrlit;` — fill in the inferred length from the
|
||||
* initialiser. `resolve_type` left alen=0 as a sentinel. */
|
||||
if (declared && declared->kind == TY_ARRAY && declared->alen == 0 &&
|
||||
initt) {
|
||||
Type *iu = (initt->kind == TY_NAMED) ? initt->under : initt;
|
||||
* initialiser. `resolve_type` left alen=0 as a sentinel. A `[_]T`
|
||||
* with no array-literal initialiser (no init at all, or a non-array
|
||||
* init) can't infer its length — that is a loud error, never a
|
||||
* silent zero-length array (rule 7, #7). */
|
||||
if (declared && declared->kind == TY_ARRAY && declared->alen == 0) {
|
||||
Type *iu = initt
|
||||
? ((initt->kind == TY_NAMED) ? initt->under : initt)
|
||||
: NULL;
|
||||
if (iu && iu->kind == TY_ARRAY)
|
||||
declared = type_array(c->a, declared->sub, iu->alen);
|
||||
else
|
||||
@@ -2545,12 +2549,46 @@ check_file(Checker *c, Node *file)
|
||||
case N_LET: {
|
||||
if (d->rhs) {
|
||||
Type *rt = cexpr(c, d->rhs);
|
||||
/* `let xs: [_]T = arrlit;` at module level — infer the
|
||||
* length from the initialiser, the same patch clet
|
||||
* applies for a local let (#7). pass-1.5 resolve_type
|
||||
* left alen=0 as the sentinel; patching d->type feeds
|
||||
* cgen's letvars registration (lv->type = d->type),
|
||||
* which both lays the full-length DATA row and reads
|
||||
* the right `.len`. */
|
||||
if (d->type && d->type->kind == TY_ARRAY
|
||||
&& d->type->alen == 0) {
|
||||
Type *iu = rt
|
||||
? ((rt->kind == TY_NAMED) ? rt->under : rt)
|
||||
: NULL;
|
||||
if (iu && iu->kind == TY_ARRAY) {
|
||||
d->type = type_array(c->a,
|
||||
d->type->sub, iu->alen);
|
||||
/* The Sym installed in pass-1.5 still
|
||||
* carries the alen=0 sentinel; a later
|
||||
* `x.len` resolves `x` through the Sym
|
||||
* (its type stamps n->lhs->type, which
|
||||
* cgen reads as u->alen). Re-point it at
|
||||
* the inferred-length type too. */
|
||||
Sym *s = scope_lookup_local(c->cur,
|
||||
d->str);
|
||||
if (s) s->type = d->type;
|
||||
} else
|
||||
err(c, d->pos, "[_]T needs an "
|
||||
"array-literal initialiser");
|
||||
}
|
||||
if (d->type == NULL) d->type = type_default(rt);
|
||||
if (d->type && rt != ty_err && d->type != ty_err
|
||||
&& !type_assignable(d->type, rt)
|
||||
&& !arrlit_init_fits(c, d->type, d->rhs))
|
||||
err(c, d->pos, "let %s init not assignable",
|
||||
d->str);
|
||||
} else if (d->type && d->type->kind == TY_ARRAY
|
||||
&& d->type->alen == 0) {
|
||||
/* `let x: [_]T;` — no initialiser, length can't be
|
||||
* inferred (rule 7, #7). */
|
||||
err(c, d->pos, "[_]T needs an array-literal "
|
||||
"initialiser");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user