wcc: Hare-style !T error marker on tagged-union variants
A type prefixed with `!` is flagged as an error variant. When any variant in a tagged union carries the flag, `?` propagation uses those (and only those) as the error subset; the unflagged variant is the success type. The legacy "first variant = success" rule still applies when no `!`-flag is present, so existing code keeps working. - TK_NOT in parsetype → N_TBANG wrapper (lhs = inner type expr). Appended to Nkind tail for wwdump-diff byte stability. - resolve_type N_TBANG: wraps primitives in a fresh Type copy so the iserror bit doesn't taint shared globals like ty_str/ty_i32; flips the bit in place on NAMED (already unique per alias decl). - Type.iserror; type_named and typedecl inherit it from under. - New check.c helpers: tagged_has_errflag, tagged_is_error_variant, tagged_success_type. N_TRYPROP uses them to find the error subset and verify each error variant is propagatable to the enclosing return. - cgen mirrors with cg_tagged_success_tag + cg_variant_is_error. `?` compares AX against the success tag (no longer always 0) and remaps each error variant's tag for the enclosing fn. `!` aborts on any non-success tag. strconv.invalid and strconv.overflow now use `!`-flagged shape (`!i32` and `!void`) — visible signal in the API surface that they are error types, matching Hare. The (i64 | invalid | overflow) return shape and behavior are unchanged for callers; their match arms still bind the same way. Selfhost: lib/ww/parse/parse.ww recognises `!T` and emits N_TBANG. The selfhost typechecker and cgen ignore the flag — none of the selfhost sources use `!`, so byte-identity gates are unaffected. The selfhost mirror catches up when there's a source using it.
This commit is contained in:
@@ -170,6 +170,45 @@ cg_variant_match(Type *vt, Type *src)
|
||||
return type_eq(vt, src);
|
||||
}
|
||||
|
||||
/* cg_tagged_success_tag — index of the success variant in a tagged
|
||||
* union. Mirrors check.c tagged_success_type: explicit-flag mode
|
||||
* picks the first non-`!`-marked variant; legacy mode picks index 0. */
|
||||
static int
|
||||
cg_tagged_success_tag(Type *t)
|
||||
{
|
||||
if (t == NULL) return 0;
|
||||
if (t->kind == TY_NAMED) t = t->under;
|
||||
if (t == NULL || t->kind != TY_TAGGED) return 0;
|
||||
int has_err = 0;
|
||||
for (Tparam *p = t->params; p; p = p->next)
|
||||
if (p->type && p->type->iserror) { has_err = 1; break; }
|
||||
if (!has_err) return 0;
|
||||
int idx = 0;
|
||||
for (Tparam *p = t->params; p; p = p->next, idx++)
|
||||
if (p->type && !p->type->iserror) return idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cg_variant_is_error(Type *t, int idx)
|
||||
{
|
||||
if (t == NULL) return 0;
|
||||
if (t->kind == TY_NAMED) t = t->under;
|
||||
if (t == NULL || t->kind != TY_TAGGED) return 0;
|
||||
int has_err = 0;
|
||||
for (Tparam *p = t->params; p; p = p->next)
|
||||
if (p->type && p->type->iserror) { has_err = 1; break; }
|
||||
int i = 0;
|
||||
for (Tparam *p = t->params; p; p = p->next, i++) {
|
||||
if (i == idx) {
|
||||
if (has_err) return p->type && p->type->iserror;
|
||||
/* legacy: index 0 is success, rest are errors */
|
||||
return idx != 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the variant-tag index of `vt` inside the tagged-union type `t`.
|
||||
* Returns -1 if `t` is not tagged or `vt` does not match a variant.
|
||||
* Used by N_MATCH dispatch and by the let/assign/return tag synthesis. */
|
||||
@@ -1905,32 +1944,32 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
case N_TRYPROP: {
|
||||
/* Evaluate tagged value: AX=tag, DX=value0[, CX=value1].
|
||||
* If tag != 0, propagate as the current function's return.
|
||||
* On success, unwrap to the success-variant ABI: ≤8B values
|
||||
* land in AX; str values land in (AX=ptr, BX=len).
|
||||
*
|
||||
* Tag remap: when operand and enclosing fn have different
|
||||
* variant orderings, the operand's error tag must be
|
||||
* translated to the enclosing fn's tag for the same variant
|
||||
* type. For each non-first variant V_i in operand at index
|
||||
* i, if i != enclosing's index for V (call it j), emit a
|
||||
* conditional MOV $j → AX. Identity cases emit nothing. */
|
||||
* If the tag matches an error variant, propagate as the
|
||||
* current function's return (with a tag remap to the
|
||||
* enclosing fn's variant order). On success, unwrap to the
|
||||
* success-variant ABI: ≤8B values in AX; str values in
|
||||
* (AX=ptr, BX=len). */
|
||||
cgexpr(c, n->lhs, locals);
|
||||
Type *u = n->lhs ? n->lhs->type : NULL;
|
||||
if (u && u->kind == TY_NAMED) u = u->under;
|
||||
Type *r = cg_ret_type;
|
||||
if (r && r->kind == TY_NAMED) r = r->under;
|
||||
Type *first = (u && u->kind == TY_TAGGED && u->params)
|
||||
? u->params->type : NULL;
|
||||
int success_is_str = type_isstr(first);
|
||||
int s_tag = cg_tagged_success_tag(u);
|
||||
Type *succ_t = NULL;
|
||||
if (u && u->kind == TY_TAGGED) {
|
||||
int i = 0;
|
||||
for (Tparam *p = u->params; p; p = p->next, i++)
|
||||
if (i == s_tag) { succ_t = p->type; break; }
|
||||
}
|
||||
int success_is_str = type_isstr(succ_t);
|
||||
char *cont = mklabel(c, "tryprop_ok");
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins2(c, A_CMPQ, aimm(s_tag), areg(D_AX));
|
||||
ins1(c, A_JE, abranch(cont));
|
||||
if (u && r && r->kind == TY_TAGGED && u->params) {
|
||||
char *propret = mklabel(c, "tryprop_ret");
|
||||
int i = 1;
|
||||
for (Tparam *p = u->params->next; p;
|
||||
p = p->next, i++) {
|
||||
int i = 0;
|
||||
for (Tparam *p = u->params; p; p = p->next, i++) {
|
||||
if (!cg_variant_is_error(u, i)) continue;
|
||||
int j = cg_tag_for_variant(r, p->type);
|
||||
if (j < 0) j = 0;
|
||||
if (j == i) continue;
|
||||
@@ -1957,11 +1996,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
cgexpr(c, n->lhs, locals);
|
||||
Type *u = n->lhs ? n->lhs->type : NULL;
|
||||
if (u && u->kind == TY_NAMED) u = u->under;
|
||||
Type *first = (u && u->kind == TY_TAGGED && u->params)
|
||||
? u->params->type : NULL;
|
||||
int success_is_str = type_isstr(first);
|
||||
int s_tag = cg_tagged_success_tag(u);
|
||||
Type *succ_t = NULL;
|
||||
if (u && u->kind == TY_TAGGED) {
|
||||
int i = 0;
|
||||
for (Tparam *p = u->params; p; p = p->next, i++)
|
||||
if (i == s_tag) { succ_t = p->type; break; }
|
||||
}
|
||||
int success_is_str = type_isstr(succ_t);
|
||||
char *cont = mklabel(c, "tryunw_ok");
|
||||
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
|
||||
ins2(c, A_CMPQ, aimm(s_tag), areg(D_AX));
|
||||
ins1(c, A_JE, abranch(cont));
|
||||
ins2(c, A_MOVQ, aimm(1), areg(D_DI));
|
||||
ins2(c, A_MOVQ, aimm(60), areg(D_AX));
|
||||
|
||||
Reference in New Issue
Block a user