wcc/check: #6 stamp inferred-type array global so wwstage compiles it (was asserttyped exit 1)

An inferred-type array global -- let xs = [1,2,3]; -- hard-failed wwstage
with 'asserttyped: int' exit 1, while cstage compiled+ran it. The
array-twin of the inferred-global family (#135 inferred-float, #150-B
inferred-Sym-repoint).

exprtype's N_ARRLIT arm synthesizes the array type for an unannotated
literal but left two synthesized child nodes unstamped: the count literal
(asserttyped trips on it -> the loud failure) and the element TNAME (cgen
then drops a non-scalar element's header load -> the silent miscompile
that merely accepting on alone would introduce: an inferred str-array's
xs[1].len read 24 not 3). Both are now stamped at the synthesis site:
cn.type_ (asserttyped facet) and elt.type_ (cgen facet). Inferred int /
u8 / str / struct / multi-dim array globals + locals + args now compile
byte-identically to the explicit-typed form (== cstage).

cstage unchanged (w6c md5 unchanged); selfhost has no inferred array
globals so byte-id 990-997 8/8, no lib pin flips. test/wcc/833.
This commit is contained in:
2026-06-09 02:59:52 +09:00
parent dfdf99ffd8
commit 46e8354056
5 changed files with 310 additions and 0 deletions

View File

@@ -13651,9 +13651,29 @@ fn exprtype(c: *checker, e: *node, hint: *node) *node = {
// check.c:1859 empty-fallback ty_int (#103). Was "i32".
if (elt == nil) { elt = mktname(c, "int"); };
let arr: *node = newnode(nkind.N_TARRAY, "", 0, 0);
// #6(niche): stamp the SYNTHESIZED element TNAME so the inferred
// array's cgen is IDENTICAL to an explicit [N]T's (whose element is
// resolvewalk-stamped). For a scalar element (int/u8) cgen's
// fallback reads correctly with a nil elt.type_ (byte-id either
// way), but a multi-word element (str, 24B) needs the resolved
// element tinfo to emit the 3-word header element load — without it
// cgen drops to the 8B-scalar path and `xs[i].len` reads the slot
// stride, not the length (silent cs!=ww). Idempotent: elt may be a
// real exprtype result whose type_ is already set.
if (elt.type_ == nil) { elt.type_ = tinfofornode(c, elt): *void; };
arr.lhs = elt;
let cn: *node = newnode(nkind.N_INTLIT, "", 0, 0);
cn.uval = count;
// #6(niche): stamp the SYNTHESIZED count literal. When this arr is
// planted on an inferred array global's n.lhs (the array twin of
// #135/#150-B), asserttyped walks arr.rhs (this N_INTLIT, an expr
// node) and trips `asserttyped: int` — an explicit [N]T's count is
// resolvewalk-stamped, the synthesized inferred one wasn't. The
// element TNAME (arr.lhs) needs no stamp: N_TNAME is not an
// asserttyped expr kind. tinfofornode only resolves type-kind
// nodes, so type the count off a fresh `int` TNAME; cgen reads
// arr.rhs.uval, so this stamp is byte-id-inert.
cn.type_ = tinfofornode(c, mktname(c, "int")): *void;
arr.rhs = cn;
e.type_ = tinfofornode(c, arr): *void;
// #103: stamp the synthesized array NODE too. checkletassign