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:
@@ -135,6 +135,16 @@ static const struct row rows[] = {
|
||||
{ "fn inner() (i32 | str | bool) = { return 1; }; "
|
||||
"fn outer() (i64 | bool | str) = { let v: i32 = inner()?; return v: i64; };",
|
||||
"ok" }, /* reversed-order error subset is OK */
|
||||
|
||||
/* `!`-flagged error variants (Hare's explicit error marker) */
|
||||
{ "type invalid = !i32; "
|
||||
"fn parse() (i64 | invalid) = { return 5: invalid; }; "
|
||||
"fn caller() (i64 | invalid) = { let v: i64 = parse()?; return v + 1; };",
|
||||
"ok" },
|
||||
{ "type invalid = !i32; type overflow = !void; "
|
||||
"fn parse() (invalid | i64 | overflow) = { return 0i64; }; "
|
||||
"fn caller() i32 = { let v: i64 = parse()?; return v: i32; };",
|
||||
"enclosing function must return a tagged union" },
|
||||
};
|
||||
|
||||
int
|
||||
|
||||
@@ -538,6 +538,29 @@ static const struct row rows[] = {
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 3 },
|
||||
/* `!`-flagged error variants: success picked by absence of `!`,
|
||||
* errors picked by presence. Tag remap still works across
|
||||
* different variant orders between operand and enclosing fn. */
|
||||
{ "type invalid = !i32;\n"
|
||||
"type overflow = !void;\n"
|
||||
"fn inner(n: i32) (invalid | i64 | overflow) = {\n"
|
||||
" if (n == 0) { return 7: invalid; };\n"
|
||||
" if (n < 0) { return void: overflow; };\n"
|
||||
" return n: i64 + 1000;\n"
|
||||
"};\n"
|
||||
"fn outer(n: i32) (overflow | i64 | invalid) = {\n"
|
||||
" let v: i64 = inner(n)?;\n"
|
||||
" return v + 1;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: (overflow | i64 | invalid) = outer(0);\n"
|
||||
" match (r) {\n"
|
||||
" case let v: i64 => return v: i32;\n"
|
||||
" case let e: invalid => return e + 100;\n"
|
||||
" case let e: overflow => return 999;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 107 },
|
||||
/* ? with reversed error variant order: operand (i32 | str | bool),
|
||||
* enclosing (i64 | bool | str). Tag remap maps str:1→2, bool:2→1. */
|
||||
{ "fn inner(n: i32) (i32 | str | bool) = {\n"
|
||||
|
||||
Reference in New Issue
Block a user