The selfhost checker did name resolution only — anything tagged-
union-shaped sailed through silently. The C check.c implements
three structural checks; this commit mirrors them at the AST level
in selfhost/cmd/wcc/check.ww:
1. Match exhaustiveness: every variant of the scrutinee's tagged
union must be covered by a case arm (incl. multi-pattern alts)
or a default arm. Operates on the scrutinee's declared type
(N_TTAGGED via N_IDENT's sym.decl.lhs).
2. ? subset propagation: each error variant of the operand's type
must be a variant of the enclosing fn's return type. Enclosing
return must itself be a tagged union when the operand has any
errors.
3. !-flag semantics: in flag-aware mode (any variant marked `!T`),
error subset = flagged variants. Legacy mode (no flags) =
everything-but-first. is_error_variant unifies both rules.
No tinfo / type-inference work: the checks read declared AST type
nodes directly. `resolvealias` chases N_TNAME → typedecl body to
handle aliased tagged unions. `type_eq_ast` does structural
comparison on the subset of type-expression shapes the checks
encounter (TNAME by string, TPTR/TSLICE/TCHAN recursive).
Folded into resolvewalk rather than a separate second pass, so the
checks see the same per-statement scope state as resolve. fnret is
threaded through resolvefnbody so ? can find the enclosing return.
New test/wcc/950_selfcheck.c — five rows exercising each error path
(missing variant, non-tagged enclosing, missing error subset
member, the flag-aware happy path, the flag-aware missing-error
case). Test suite now reports 21 ok.