wcc+w6c_ww: loud-gate try-propagation over multi-success unions (F8/F9 interim)

? and ! assume ONE success member end-to-end: the checker collapses
the result to the first non-error variant (check.c tagged_success_type
/ check.ww exprtype) and cgen emits a single tag compare, so any other
success member is silently mistaken for an error — ? propagates it to
the caller (p11h: []capture read back as nomem, exit 21), ! aborts on
it. Until the honest subset-union result typing lands (task #14, harec
check.c:2759-2835), both stages loud-reject |success| > 1 at the
checker choke-points (one per stage), identical diagnostic, both ops
per rob's one-class ruling (#133 precedent). (T|err1|err2) — one
success, many errors — stays legal (925 canary + new accept rows).

F9 rides along (task #12): wwstage scruttype only resolves IDENT/DOT,
so the direct forms f()? is T / match(f()?) / f()! is T slipped its
lenient-miss contract and were silently ACCEPTED where cstage rejects
(cs!=ww, gate-blind). checkisas/checkmatchexhaust now resolve the
try-result via exprtype, keyed on the RESOLVED success type — a named
tagged success ((ab|nomem)? is i32) keeps being accepted, matching
cstage's verdict empirically.

test/wcc/806: 11 rows x dual driver + byte-id accepts (26 fixtures);
reject rows pin exact per-stage diagnostic text; p11h + q_card2_unw
graduated to rejects; call-arg-position reject + void-success accept
pin position-independence and the dominant lib/ (void|err)? shape.
Tasks #5 + #12; #14 lifts both gates together.
This commit is contained in:
2026-06-04 10:10:35 +09:00
parent cfc2985c61
commit 48df04a8ca
6 changed files with 686 additions and 20 deletions

View File

@@ -1887,11 +1887,26 @@ cexpr(Checker *c, Node *n)
* (so the caller can match on it). cgen does the tag remap.
* For ! : no propagation, so no subset check. */
Type *succ = tagged_success_type(u);
int has_errors = 0;
for (Tparam *p = u->params; p; p = p->next)
if (tagged_is_error_variant(u, p->type)) {
has_errors = 1; break;
}
int has_errors = 0, nsucc = 0;
for (Tparam *p = u->params; p; p = p->next) {
if (tagged_is_error_variant(u, p->type))
has_errors = 1;
else
nsucc++;
}
/* F8 interim gate (task #5): try-propagation assumes ONE
* success member end-to-end — succ collapses to the first
* non-error variant and cgen emits a single tag compare, so
* any OTHER success member is silently mistaken for an error
* (? propagates it; ! aborts on it). One class, both ops
* (#133 precedent). Until the honest subset-union result
* typing lands (task #14, harec check.c:2759-2835), reject
* loud. */
if (nsucc > 1) {
return n->type = err(c, n->pos,
"%s: multi-success union unwired (task #14): bind and match instead",
n->kind == N_TRYPROP ? "?" : "!");
}
if (n->kind == N_TRYPROP && has_errors) {
Type *r = c->ret;
Type *ru = (r && r->kind == TY_NAMED) ? r->under : r;