ww: add Hare-style is/as postfix ops on tagged unions
`e is T` returns bool (variant tag == T's index); `e as T` unwraps to T or exit(1) on mismatch. Postfix, same precedence as `:` cast. TK_IS / N_TYPETEST / N_TYPEASSERT appended at the tail of their enums so every prior numeric value stays unchanged — the 990_selfhost wwdump-diff stays byte-clean. Cgen mirrors the match-case slot-based load (tag at +0, value at +8/+16), so an N_IDENT tagged-union local works just like a match scrutinee. Selfhost cgen inlines the slot resolution because the wwstage cgen drops sign bits on `*i32` output parameters in this position. Renames `errors.is` -> `errors.equal` (the only naming collision; the existing comment already noted it shared shape with strings.equal/bytes.equal).
This commit is contained in:
@@ -710,6 +710,48 @@ cexpr(Checker *c, Node *n)
|
||||
n->type = ty_void;
|
||||
return n->type;
|
||||
}
|
||||
case N_TYPETEST: case N_TYPEASSERT: {
|
||||
/* `e is T` → bool; `e as T` → T.
|
||||
* Requires lhs to be a tagged union and T to be one of its
|
||||
* variants. The variant-index lookup lives in cgen (it knows
|
||||
* NAMED-vs-structural matching for the success-variant rules);
|
||||
* here we just check the LHS shape and resolve T. */
|
||||
Type *t = cexpr(c, n->lhs);
|
||||
Type *vt = resolve_type(c, n->rhs);
|
||||
/* Stash the variant on rhs->type — cgen reads it uniformly
|
||||
* whether the expression returns bool (is) or the variant
|
||||
* itself (as). */
|
||||
if (n->rhs) n->rhs->type = vt;
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
if (u == NULL || u->kind != TY_TAGGED) {
|
||||
const char *op = (n->kind == N_TYPETEST) ? "is" : "as";
|
||||
return n->type = err(c, n->pos,
|
||||
"%s on non-tagged-union %s", op,
|
||||
type_name(c->a, t));
|
||||
}
|
||||
/* Diagnostic-only: verify T appears as a variant. Mirrors
|
||||
* cg_variant_match (NAMED ≡ pointer-identical, otherwise
|
||||
* structural). Skipped silently if vt is ty_err. */
|
||||
if (vt && vt != ty_err) {
|
||||
int found = 0;
|
||||
for (Tparam *p = u->params; p; p = p->next) {
|
||||
if (p->type == NULL) continue;
|
||||
if (p->type->kind == TY_NAMED &&
|
||||
vt->kind == TY_NAMED) {
|
||||
if (p->type == vt) { found = 1; break; }
|
||||
} else if (p->type->kind == TY_NAMED ||
|
||||
vt->kind == TY_NAMED) {
|
||||
continue;
|
||||
} else if (type_eq(p->type, vt)) {
|
||||
found = 1; break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
err(c, n->pos, "%s is not a variant of %s",
|
||||
type_name(c->a, vt), type_name(c->a, t));
|
||||
}
|
||||
return n->type = (n->kind == N_TYPETEST) ? ty_bool : vt;
|
||||
}
|
||||
case N_TRYPROP: case N_TRYUNW: {
|
||||
Type *t = cexpr(c, n->lhs);
|
||||
Type *u = (t && t->kind == TY_NAMED) ? t->under : t;
|
||||
|
||||
Reference in New Issue
Block a user