cgen: #95 c3 reviewer-fold — is/as gate exact-only, no widening leak

c1/c2 widened flatvariantidxt (selfhost) with the chain + structural
tag-synthesis arms and a >=2 ambiguity os.exit, scoped to the cgen
WIDEN consumer. But flatvariantidxt is a choke-point: the wwstage is/as
ACCEPTANCE gate (check.ww:4677, the #198 spread fallback) reuses it, so
the widening leaked into checker acceptance — vs base 329481c:
  * `let v:(void|ali)=…; v is base` (ali=base): cstage rejects, wwstage
    ACCEPTED+built — new cs!=ww acceptance divergence (rule-10 break);
  * `(void|tb)`, `v is ta` (unrelated same-layout): same leak via the c2
    structural arm;
  * `(ali|ali2)`, `v is base`: wwstage DIED with the cgen-internal fatal
    "flatvariantidxt: source alias chain reaches >=2 variants" DURING
    CHECK — a cgen diag surfacing in the checker (layering).
cstage is unaffected: its is/as gate (check.c:2036) is independent of
cg_tag_for_variant (cgen-phase only), so the fuse was already broken at
this site — the cgen-helper change moved wwstage's CHECKER but not
cstage's. This contradicts the #95 fold scope ("cgen-tag fold, no
acceptance change except the ambiguity hard-error [at the widen site]").

Fix (rob-ruled): the is/as gate needs only nominal variant membership =
pass 1. Add an explicit `exactonly` mode to flatvariantidxt — the
checker caller passes true (returns after the exact loop: no chain/
structural arms, no os.exit), every cgen caller passes false (full
tag-synthesis, unchanged). Two consumers, two modes — the honest
representation, not a wrapper. cstage's cg_tag_for_variant has no twin
checker caller, so it stays full-only and is UNTOUCHED by c3 (rule-10
satisfied: the param changes no asm — cgen always passes false; the
checker now MATCHES cstage's reject). casevariantin still backs the
#198 spread fallback.

Pins (test/wcc/944_variant_chain_b95_run.c, +4 rows -> 56 checks):
  isas_chain_reject / isas_unrel_reject — BOTH stages reject the leaked
  is/as shapes (shared experr substring "not a variant"); the c1 chain +
  c2 structural arms no longer widen acceptance.
  isas_amb_reject_notcrash — the (ali|ali2)/`is base` shape rejects
  CLEANLY (the cgen fatal text would be absent -> red), NOT a crash.
  twin_prim_alias_amb — rob's obligated mixed prim/alias TWIN:
  (int | ai) ai=int, source aj=int — both share the int bottom under
  all-variants counting, so the cgen WIDEN (full mode) hard-errors
  ("source alias chain reaches >=2 variants"), pinned LOUD both stages.

The deferred question (should is/as EVER accept cgen's richer chain/
structural shapes? = a checker-strictness feature, both stages together)
is filed as task #107, explicitly NOT folded here.

Invariants: c1/c2 cgen behavior unchanged (all cgen callers pass false =
full mode); suite byte-id rows + the dissolution corpus hold. make all
0; sizelint 0; peellint 0 (the mode param adds no peel sites); combined.ww
regen idempotent; test-unit "all 295 tests passed". c3 touches ZERO
cstage bytes — cmd/w6c/cgen.c carries only the c1/c2 additions, and
cmd/wcc/check.c is unchanged from base 329481c.
This commit is contained in:
2026-06-06 08:06:37 +09:00
parent 56aac85f6f
commit fc50a27f3e
6 changed files with 131 additions and 33 deletions

View File

@@ -146,7 +146,7 @@ fn cgtagvariantidx(c: *cgen, tagged: *node, vt: *node) i32 = {
let sti: *tinfo = tagged.type_: *tinfo;
sti = tichase(sti);
if (sti != nil && sti.kind == tykind.TY_TAGGED && vt.type_ != nil) {
return flatvariantidxt(sti, vt.type_: *tinfo);
return flatvariantidxt(sti, vt.type_: *tinfo, false);
};
return -1;
};
@@ -335,7 +335,7 @@ fn cgtryprop(c: *cgen, n: *node) void = {
let i: i32 = 0;
for (p != nil) {
if (p.iserror) {
let j: i32 = flatvariantidxt(r, p.type_);
let j: i32 = flatvariantidxt(r, p.type_, false);
if (j < 0) { j = 0; };
if (j != i) {
let skip: str = mklabel(c, "tryprop_skip");
@@ -2721,7 +2721,7 @@ fn cgmatch(c: *cgen, n: *node) void = {
if (typeisslice(pattype)) {
r = flatslicevariantidx(c, scrutt, pat.lhs);
} else {
r = flatvariantidxt(scrutt.type_: *tinfo, pattype);
r = flatvariantidxt(scrutt.type_: *tinfo, pattype, false);
};
};
if (r >= 0) { want = r; };