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:
@@ -1808,50 +1808,10 @@ fn inferletcalltype(c: *cgen, rhs: *node) *node = {
|
||||
// tagged-init branch writes past the local and tramples the next
|
||||
// slot.
|
||||
export fn letslotsize(c: *cgen, n: *node) i32 = {
|
||||
// `[_]T = arrlit;` — inferred-length array. slotsize would
|
||||
// return elem_size * 1 (treating missing length as 1); intercept
|
||||
// and compute the real count first.
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_TARRAY) {
|
||||
if (n.lhs.rhs == nil) {
|
||||
if (n.rhs != nil) {
|
||||
if (n.rhs.kind == nkind.N_ARRLIT) {
|
||||
let elemn: *node = n.lhs.lhs;
|
||||
let esz: i32 = 8;
|
||||
if (elemn != nil) {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
// Composite primitive: `str` is 16B
|
||||
// (ptr+len) — primsize returns 0 for
|
||||
// it, so it'd slot 8B without this.
|
||||
if (streq(elemn.str, "str")) {
|
||||
esz = primtypesize("str"): i32;
|
||||
} else {
|
||||
let ps: i32 = primsize(elemn.str);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
};
|
||||
};
|
||||
let cnt: i32 = 0;
|
||||
let e: *node = n.rhs.list;
|
||||
for (e != nil) {
|
||||
let adv: bool = true;
|
||||
if (e.kind == nkind.N_FIELD) {
|
||||
if (streq(e.str, "...")) {
|
||||
e = nil;
|
||||
adv = false;
|
||||
};
|
||||
};
|
||||
if (adv) {
|
||||
cnt += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
return esz * cnt;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// `[_]T = arrlit;` inferred-length arrays no longer need a slot-size
|
||||
// intercept here: the checker (inferarraylen, check.ww) stamps the
|
||||
// real element count onto the array type's length child before cgen
|
||||
// runs, so slotsize reads it like any explicit `[N]T` (#7).
|
||||
if (n.lhs != nil) { return slotsize(c, n.lhs); };
|
||||
// Annotation-less init: defer to the call's return type if we
|
||||
// can infer it. Tagged-union returns need 24B; everything else
|
||||
|
||||
Reference in New Issue
Block a user