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 base329481c: * `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 base329481c.
This commit is contained in:
@@ -373,6 +373,62 @@ static const struct row rows[] = {
|
||||
" if (!(v is tb)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
/* ---- c3 (#107): is/as ACCEPTANCE must stay nominal-exact. cgen's
|
||||
* chain/structural tag-synthesis arms widen tag lookup at the WIDEN
|
||||
* site only; the wwstage is/as gate (check.ww:4677) reuses
|
||||
* flatvariantidxt and, pre-c3, the widening leaked into ACCEPTANCE
|
||||
* (cs!=ww) and surfaced the >=2 cgen fatal mid-check. c3 added an
|
||||
* exact-only mode for that caller. These pin BOTH stages REJECTING
|
||||
* (cstage's independent gate check.c:2036 already does; wwstage now
|
||||
* matches it) — NOT a crash for the ambiguity shape. Shared experr
|
||||
* substring "not a variant" (cstage "X is not a variant of (...)",
|
||||
* wwstage "is/as: not a variant of operand (X)"). */
|
||||
{ "isas_chain_reject",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"fn f(v: (void | ali)) i32 = {\n"
|
||||
" if (v is base) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
0, 0, K_BUILDERR, "not a variant" },
|
||||
{ "isas_unrel_reject",
|
||||
"package main;\n"
|
||||
"type ta = struct { a: int, b: int };\n"
|
||||
"type tb = struct { a: int, b: int };\n"
|
||||
"fn f(v: (void | tb)) i32 = {\n"
|
||||
" if (v is ta) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
0, 0, K_BUILDERR, "not a variant" },
|
||||
{ "isas_amb_reject_notcrash",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"fn f(v: (ali | ali2)) i32 = {\n"
|
||||
" if (v is base) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
0, 0, K_BUILDERR, "not a variant" },
|
||||
/* ---- rob's obligated mixed prim/alias TWIN: union (int | ai)
|
||||
* ai=int, source typed aj=int — under all-variants counting both the
|
||||
* bare `int` variant and `ai` share the int bottom -> the cgen widen
|
||||
* (full mode) hard-errors (harec nassign>=2 -> NULL). Pinned LOUD
|
||||
* BOTH stages (the chain-ambiguity path; #95 deviation-2 ratified). */
|
||||
{ "twin_prim_alias_amb",
|
||||
"package main;\n"
|
||||
"type ai = int;\n"
|
||||
"type aj = int;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: aj = 7;\n"
|
||||
" let v: (int | ai) = s;\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_BUILDERR,
|
||||
"source alias chain reaches >=2 variants" },
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
Reference in New Issue
Block a user