wcc: ? error-subset propagation typecheck + tag remap
`expr?` previously did a brain-dead RET through whatever AX/DX/CX held — only safe when operand and enclosing fn had identical variant ordering. Tests relied on that alignment by construction. Now: - Typecheck: each non-first variant of operand must appear as a variant of the enclosing fn's return tagged union. Enclosing must itself be tagged (a non-tagged return has no slot for errors to land in). - Cgen: on tag != 0, walk operand's error variants and emit a conditional tag remap (cmp/jne/mov/jmp) for any whose index in enclosing differs from operand's. Identity cases emit nothing, so same-shape operands cost zero extra instructions. Selfhost cgen doesn't implement N_TRYPROP at all (no selfhost source uses `?`); byte-identity tests still pass. One existing e2e row used `?` with main returning i32 — relied on the old loose semantics. Switched to `!` (abort-on-error); it was exercising success-unwrap, not propagation.
This commit is contained in:
@@ -884,8 +884,39 @@ cexpr(Checker *c, Node *n)
|
||||
n->kind == N_TRYPROP ? "?" : "!",
|
||||
type_name(c->a, t));
|
||||
}
|
||||
/* Convention: first variant is the success type. */
|
||||
/* Convention: first variant is the success type; the remaining
|
||||
* variants are the error subset.
|
||||
*
|
||||
* For ? : each error variant must be propagatable — i.e. it
|
||||
* must be a variant of the enclosing function's return type
|
||||
* (so the caller can match on it). cgen does the tag remap.
|
||||
* For ! : no propagation, so no subset check. */
|
||||
Tparam *first = u->params;
|
||||
if (n->kind == N_TRYPROP && first && first->next) {
|
||||
Type *r = c->ret;
|
||||
Type *ru = (r && r->kind == TY_NAMED) ? r->under : r;
|
||||
if (ru == NULL || ru->kind != TY_TAGGED) {
|
||||
err(c, n->pos,
|
||||
"?: enclosing function must return a tagged "
|
||||
"union to propagate errors (got %s)",
|
||||
type_name(c->a, r));
|
||||
} else {
|
||||
for (Tparam *e = first->next; e; e = e->next) {
|
||||
int ok = 0;
|
||||
for (Tparam *p = ru->params; p;
|
||||
p = p->next)
|
||||
if (variant_match(p->type,
|
||||
e->type)) {
|
||||
ok = 1; break;
|
||||
}
|
||||
if (!ok)
|
||||
err(c, n->pos,
|
||||
"?: error variant %s not in enclosing return %s",
|
||||
type_name(c->a, e->type),
|
||||
type_name(c->a, r));
|
||||
}
|
||||
}
|
||||
}
|
||||
return n->type = first ? first->type : ty_err;
|
||||
}
|
||||
case N_TUPLE: {
|
||||
|
||||
Reference in New Issue
Block a user