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:
@@ -79,6 +79,115 @@ fn cgexpr(c: *cgen, n: *node) void = {
|
||||
if (k == N_CALL) { cgcall(c, n); return; };
|
||||
|
||||
if (k == N_ASSIGN) { cgassign(c, n); return; };
|
||||
|
||||
if (k == N_TYPETEST) { cgtypetest(c, n); return; };
|
||||
if (k == N_TYPEASSERT) { cgtypeassert(c, n); return; };
|
||||
};
|
||||
|
||||
// cgtagvariantidx — find the 0-based variant index of `vt` inside the
|
||||
// tagged-union type expression `tagged`. -1 if `tagged` isn't an
|
||||
// N_TTAGGED or no variant matches. Mirrors the lookup that cgmatch
|
||||
// does inline; pulled out so `is` / `as` can reuse it.
|
||||
fn cgtagvariantidx(tagged: *node, vt: *node) i32 = {
|
||||
if (tagged == nil) { return -1; };
|
||||
if (vt == nil) { return -1; };
|
||||
if (tagged.kind != N_TTAGGED) { return -1; };
|
||||
let want: str;
|
||||
want.ptr = nil; want.len = 0;
|
||||
if (vt.kind == N_TNAME) { want = vt.str; };
|
||||
if (want.len == 0) { return -1; };
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
if (v.kind == N_TNAME) {
|
||||
if (streq(v.str, want)) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
// `e is T` — load the lhs's tag, compare against T's variant
|
||||
// index, set AX = (tag == idx). Result type is bool.
|
||||
//
|
||||
// Slot resolution is inlined (rather than factored into a helper
|
||||
// with output parameters): wwstage cgen has a trap with i32
|
||||
// stored via *i32 in this context — direct assignment of the
|
||||
// local works, indirection through &scrutoff drops sign bits.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
let nel: str = mklabel(c, "is_ne");
|
||||
let dnl: str = mklabel(c, "is_done");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(nel);
|
||||
emitline("\n\tMOVQ\t$1, AX\n\tJMP\t");
|
||||
emitline(dnl);
|
||||
emitline("\n");
|
||||
emitlabel(nel);
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
emitlabel(dnl);
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
// `e as T` — load tag, abort (exit 1) if tag != T's variant
|
||||
// index, otherwise unwrap to T's ABI: scalar/ptr → AX, 16B
|
||||
// str → (AX, BX). Mirrors cgmatch's slot-based value load.
|
||||
// Slot resolution inlined; see cgtypetest comment.
|
||||
let lhs: *node = n.lhs;
|
||||
let scrutoff: i32 = 0;
|
||||
let scrutt: *node = nil;
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == N_IDENT) {
|
||||
let lc: *local = localfindnode(c, lhs.str);
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetype(c, lc.tnode);
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(scrutt, n.rhs);
|
||||
if (want < 0) { want = 0; };
|
||||
let okl: str = mklabel(c, "asrt_ok");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(scrutoff: i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(want: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJE\t");
|
||||
emitline(okl);
|
||||
emitline("\n\tMOVQ\t$1, DI\n\tMOVQ\t$60, AX\n\tSYSCALL\n");
|
||||
emitlabel(okl);
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
if (isstrtype(c, n.rhs)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scrutoff + 16): i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||
|
||||
Reference in New Issue
Block a user