w6c+w6c_ww: box [N]tagged array-literal elements via the tagged-store path (fix #12)

A [N]tagged-union array-literal element fell through the is_agg
multi-word-copy path (STRUCT/ARRAY/TUPLE/str/slice only) to the scalar
1-word store: the raw value landed in word 0 (the tag slot) with no tag
written and no payload boxed, so a later match found no variant. Both
stages under-copied identically, so the copy-depth bug was byte-id-blind
— a stride-only fix would still store 1 word and pass the gate green on
both-wrong.

Route each tagged element through cg_widen_tagged_store / the N_LET "BP"
tagged-store wrapper — the same choke-point let-init, vararg gather and
struct-field stores already use — so boxing, tag-remap and zero-pad-to-
slot come for free. esz now comes from the stamped slot size (rule-13);
the wwstage narrow override only covered widths 1/2/4, leaving a 16/24B
tagged element on the wrong 8-byte sentinel stride. rule-7 loud-stops
the unwired `[N]tagged=[x...]` repeat-fill (the widen call consumes the
node and trashes AX).

test/wcc/685: table-driven runtime readback (106/42/13) + a build-fail
row for the repeat-fill loud-stop, both stages.
This commit is contained in:
2026-06-03 20:32:22 +09:00
parent 3f04ea15ef
commit 63142770de
6 changed files with 475 additions and 3 deletions

View File

@@ -9059,6 +9059,13 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
int is_agg = esubu && (esubu->kind == TY_STRUCT
|| esubu->kind == TY_ARRAY
|| esubu->kind == TY_TUPLE);
/* #12: a tagged-union element. NOT folded into is_agg —
* is_agg's body does N_STRUCTLIT/N_IDENT word-copy and
* FATALs on the literal/scalar case, never boxing the
* tag+payload. Route each element through the same
* cg_widen_tagged_store choke-point every other tagged
* store uses (let-init, vararg gather, struct-field). */
int is_tagged_el = esubu && esubu->kind == TY_TAGGED;
int is_str_el = type_isstr(esub);
/* #20/#270 str-slice arm: a slice element is a 24B
* {ptr,len,cap} header just like str; cgexpr lowers it
@@ -9144,6 +9151,13 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
idx++;
continue;
}
if (is_tagged_el) {
cg_widen_tagged_store(c, locals, esub,
e, D_BP, base, esz);
last = e;
idx++;
continue;
}
cgexpr(c, e, *locals);
if (is_str_el || is_slice_el) {
ins2(c, A_MOVQ, areg(D_AX),
@@ -9165,6 +9179,12 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
if (repeat && is_agg)
fatal("#270-1c: `...` repeat of an aggregate "
"array-literal element not wired (rule-7)");
/* #12: `...` re-stores from AX, but cg_widen_tagged_store
* consumed the node and trashed AX — a repeat-fill would
* write garbage. No consumer needs `[N]tagged=[x,...]`. */
if (repeat && is_tagged_el)
fatal("#12: `...` repeat of a tagged-union "
"array-literal element not wired (rule-7)");
if (repeat && last) {
/* fill remaining slots with the value still in
* AX (and BX for str). */