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:
@@ -162,6 +162,11 @@ main(void)
|
||||
"fn try() (i32, str) = { return 0, \"\"; };",
|
||||
"fn use_tuple() void = { let q, r = divmod(10, 3); };",
|
||||
"fn assign_tuple() void = { q, r = divmod(10, 3); };",
|
||||
|
||||
/* Hare-style type test / type assertion (postfix) */
|
||||
"fn ti(r: (i64 | i32)) bool = { return r is i64; };",
|
||||
"fn ai(r: (i64 | i32)) i64 = { return r as i64; };",
|
||||
"fn br(r: (i64 | str)) i32 = { if (r is i64) { return 1; }; return 0; };",
|
||||
};
|
||||
int n = sizeof parses / sizeof parses[0];
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
||||
@@ -750,7 +750,7 @@ static const struct row rows[] = {
|
||||
" needle[0] = 119u8; needle[1] = 111u8; needle[2] = 114u8;\n"
|
||||
" return bytes.indexsub(buf[0:12], needle[0:3]);\n"
|
||||
"};", 7 },
|
||||
/* errors.is — sentinel comparison through a (T | error) union.
|
||||
/* errors.equal — sentinel comparison through a (T | error) union.
|
||||
* Sets up two errors, dispatches each, and confirms the matching
|
||||
* sentinel detection. */
|
||||
{ "use errors;\n"
|
||||
@@ -766,13 +766,13 @@ static const struct row rows[] = {
|
||||
" match (r1) {\n"
|
||||
" case let v: i64 => acc += -100;\n"
|
||||
" case let e: errors.error =>\n"
|
||||
" if (errors.is(e, errors.eEOF)) { acc += 1; }\n"
|
||||
" if (errors.equal(e, errors.eEOF)) { acc += 1; }\n"
|
||||
" else { acc += -100; };\n"
|
||||
" };\n"
|
||||
" match (r2) {\n"
|
||||
" case let v: i64 => acc += -100;\n"
|
||||
" case let e: errors.error =>\n"
|
||||
" if (errors.is(e, errors.eShortRead)) { acc += 10; }\n"
|
||||
" if (errors.equal(e, errors.eShortRead)) { acc += 10; }\n"
|
||||
" else { acc += -100; };\n"
|
||||
" };\n"
|
||||
" return acc;\n"
|
||||
@@ -869,6 +869,33 @@ static const struct row rows[] = {
|
||||
" os.write(1, GREETING.ptr, GREETING.len: u64);\n"
|
||||
" return GREETING.len: i32;\n"
|
||||
"};", 3 },
|
||||
/* Hare-style `is` / `as`: type test returns bool, type assertion
|
||||
* unwraps to the variant's value (success path only — abort path
|
||||
* exit(1) is exercised manually). Covers i64/i32 scalar variants
|
||||
* and str (16B variant via .len pseudo-field). */
|
||||
{ "fn classify(n: i64) (i64 | i32) = {\n"
|
||||
" if (n < 0) { return 7: i32; };\n"
|
||||
" return n;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let ok: (i64 | i32) = classify(40);\n"
|
||||
" let bad: (i64 | i32) = classify(-1);\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" if (ok is i64) { s += 1; };\n"
|
||||
" if (bad is i32) { s += 1; };\n"
|
||||
" if (ok is i32) { s += 100; };\n"
|
||||
" if (bad is i64) { s += 100; };\n"
|
||||
" let v: i64 = ok as i64;\n"
|
||||
" let e: i32 = bad as i32;\n"
|
||||
" return (v: i32) + s + e;\n"
|
||||
"};", 49 },
|
||||
/* `as` on a 16B str variant — unwrap loads (ptr, len). */
|
||||
{ "fn fail() (i64 | str) = { return \"bad\"; };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: (i64 | str) = fail();\n"
|
||||
" let e: str = r as str;\n"
|
||||
" return e.len: i32;\n"
|
||||
"};", 3 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user