wcc/ww: tagged-union normalization at tinfofornode (never-drop, dedup, collapse, nullable fold)

wwstage computed tagged sizes/tags off the raw variant list — size()
folded wrong constants (size((*u8|void)) 16 vs 8, (i32|never) 16 vs 4)
and duplicate variants got divergent tag numbering vs cstage, while
ww's own cgen layout folded nullable but its size() didn't. Make
tinfofornode's N_TTAGGED arm the normalization SSoT mirroring cstage
resolve_type (check.c:801-882): never-drop, duplicate dedup via
structural typeeq, single-variant collapse, nullable fold on the
normalized pair; astsize/astalign delegate, and voidvariantindex reads
the normalized ti.params (cgen.c:900-911) so construct/match/void tag
readers agree. Corpus-neutral (zero-move on all combineds);
989_tagnorm_run pins the folds dual-stage, red-proven. Review items
#1/#3; residual #45 filed (AST-keyed nullable gate at global emit).
This commit is contained in:
2026-06-12 07:03:45 +09:00
parent d6052e0829
commit e0df2adf47
6 changed files with 666 additions and 201 deletions

View File

@@ -2967,18 +2967,32 @@ export fn nullableptrtag(t: *node) i32 = {
};
// voidvariantindex — find the 0-based index of the `void` variant in a
// tagged-union type expr, -1 if absent. Used by cgreturn to map bare
// `return;` in a tagged-union-returning fn to the void variant's tag.
// tagged-union type, -1 if absent. Used by cgreturn to map bare `return;`
// in a tagged-union-returning fn to the void variant's tag.
//
// #1/#3 (F1 fold): reads the NORMALIZED variant chain (ti.params, which
// tinfofornode now never-drops + dedups), NOT the raw AST tagged.list. The
// construct/match tag numbering already rides ti.params, so once F1 dedups
// it, a deduped union with a void variant (e.g. `(i32|i32|void)`) would
// desync its bare-`return;` void tag from match's if this stayed AST-keyed.
// Mirrors cstage cg_tag_for_variant(rt, ty_void) (cmd/w6c/cgen.c:900-911):
// chase NAMED, scan params, match bare TY_VOID (not `!void`, carried by the
// tparam iserror flag).
fn voidvariantindex(tagged: *node) i32 = {
if (tagged == nil) { return -1; };
if (tagged.kind != nkind.N_TTAGGED) { return -1; };
let v: *node = tagged.list;
let ti: *tinfo = tagged.type_: *tinfo;
if (ti == nil) { return -1; };
ti = tichase(ti);
if (ti == nil) { return -1; };
if (ti.kind != tykind.TY_TAGGED) { return -1; };
let p: *tparam = ti.params;
let idx: i32 = 0;
for (v != nil) {
if (v.kind == nkind.N_TNAME) {
if (streq(v.str, "void")) { return idx; };
for (p != nil) {
let vt: *tinfo = p.type_;
if (vt != nil && vt.kind == tykind.TY_VOID && !p.iserror) {
return idx;
};
v = v.next;
p = p.tnext;
idx += 1;
};
return -1;