wcc: accept NAMED-variant nominal match at tagged→tagged subset (#205)

The tagged→tagged subset arm walked src's leaves against dst's flat
variant list, so `let r: (size | eof | wrapper) = e` with e: wrapper
REJECTED at cstage's checker — wrapper's leaves (unsupported, underread,
nomem) aren't direct variants of dst. Wwstage's permissive tail
accepted silently but cgen then miscompiled the tag (#199b layout-
extension family, deferred).

Mirror the concrete→tagged fix from #199 (α) at type.c:316: when src is
a NAMED-tagged wrapper and dst has a direct NAMED-tagged variant equal
to src, accept by nominal identity BEFORE the subset loop. Wwstage's
isassignable mirrors the structural insertion before the existing
`*confident = false; return true;` tail (deferred-tightening per #202).
SSoT with `is`/`as` non-recursive variant lookup (#198 family).

Cgen's tag-remap for the wrapper-as-whole case still maps src variants
to dst tag 0 — the wrapped-slot layout for `dst.tag = variant_idx,
dst.payload = src` is #199b future-work. Probe verifies checker-accept
+ runtime exit-clean only; does NOT inspect the resulting variant tag.

Probe 774_tagged_widen_named_variant.c covers 5 rows: bug-repro,
nested-wrapper, pure-leaf subset (regression), concrete-unrelated
rejection (gate), branched callee. Two sibling cgen/checker bugs
surfaced (wwstage cgwidentaggedstorebp ssz<slot_sz pad gap; wwstage
isassignable !void-alias collapse) and documented inline at the
probe-row comment, kept in #202 family.
This commit is contained in:
2026-05-29 06:18:27 +09:00
parent 3f4eeff7ff
commit 487cf91f12
6 changed files with 404 additions and 0 deletions

View File

@@ -13115,6 +13115,24 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
// return through another fn with the same shape but possibly
// a different surface spelling.
if (du.kind == nkind.N_TTAGGED && su.kind == nkind.N_TTAGGED) {
// #205: NAMED-variant nominal compare BEFORE permissive
// fallthrough. Mirror of cstage type.c type_assignable
// tagged→tagged arm (#199 α concrete→tagged sibling).
// When src is a NAMED-tagged wrapper and dst has a direct
// NAMED-tagged variant equal to src, accept by nominal
// identity — wrapper's leaves are NOT direct variants of
// dst, so a structural subset walk would reject. SSoT
// with `is`/`as` variant lookup (#198 family).
let v: *node = du.list;
for (v != nil) {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (vu.kind == nkind.N_TTAGGED) {
if (typeeqast(v, src)) { return true; };
};
};
v = v.next;
};
*confident = false;
return true;
};

View File

@@ -3085,6 +3085,24 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
// return through another fn with the same shape but possibly
// a different surface spelling.
if (du.kind == nkind.N_TTAGGED && su.kind == nkind.N_TTAGGED) {
// #205: NAMED-variant nominal compare BEFORE permissive
// fallthrough. Mirror of cstage type.c type_assignable
// tagged→tagged arm (#199 α concrete→tagged sibling).
// When src is a NAMED-tagged wrapper and dst has a direct
// NAMED-tagged variant equal to src, accept by nominal
// identity — wrapper's leaves are NOT direct variants of
// dst, so a structural subset walk would reject. SSoT
// with `is`/`as` variant lookup (#198 family).
let v: *node = du.list;
for (v != nil) {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (vu.kind == nkind.N_TTAGGED) {
if (typeeqast(v, src)) { return true; };
};
};
v = v.next;
};
*confident = false;
return true;
};

View File

@@ -13115,6 +13115,24 @@ fn isassignable(c: *checker, dst: *node, src: *node, confident: *bool) bool = {
// return through another fn with the same shape but possibly
// a different surface spelling.
if (du.kind == nkind.N_TTAGGED && su.kind == nkind.N_TTAGGED) {
// #205: NAMED-variant nominal compare BEFORE permissive
// fallthrough. Mirror of cstage type.c type_assignable
// tagged→tagged arm (#199 α concrete→tagged sibling).
// When src is a NAMED-tagged wrapper and dst has a direct
// NAMED-tagged variant equal to src, accept by nominal
// identity — wrapper's leaves are NOT direct variants of
// dst, so a structural subset walk would reject. SSoT
// with `is`/`as` variant lookup (#198 family).
let v: *node = du.list;
for (v != nil) {
let vu: *node = resolvealias(c, unwrapbang(v));
if (vu != nil) {
if (vu.kind == nkind.N_TTAGGED) {
if (typeeqast(v, src)) { return true; };
};
};
v = v.next;
};
*confident = false;
return true;
};