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

@@ -326,6 +326,20 @@ type_assignable(Type *dst, Type *src)
}
if (du && du->kind == TY_TAGGED &&
su && su->kind == TY_TAGGED) {
/* #205: NAMED-variant nominal compare BEFORE subset.
* Mirror of the concrete→tagged arm at :316-324 (#199 α).
* When src is a NAMED-tagged wrapper and dst has a direct
* NAMED-tagged variant equal to src, accept by nominal
* identity without recursing into src's variants — those
* are wrapper's leaves, not direct variants of dst, so
* the subset loop below would reject. SSoT with `is`/`as`
* variant lookup (#198 family). */
for (Tparam *dp = du->params; dp; dp = dp->next) {
Type *pu = (dp->type && dp->type->kind == TY_NAMED)
? dp->type->under : dp->type;
if (pu && pu->kind == TY_TAGGED &&
type_eq(dp->type, src)) return 1;
}
for (Tparam *sp = su->params; sp; sp = sp->next) {
int ok = 0;
for (Tparam *dp = du->params; dp; dp = dp->next)