plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


d03136b — Peter John 2026-10-27T11:17:47+05:30
docs(plans): Zig error values/sets — error.Name, return, error X = A | B, X!T
docs/plans/2026-10-27-zig-error-sets.md ADDED
@@ -0,0 +1,42 @@
1
+ # Zig error values and sets: `error.Name`, `return`, `error X = A | B`, `X!T`
2
+
3
+ **Status:** planned. Waiting for the user to verify it before it runs.
4
+ **Builds on:** `2026-10-27-zig-errors.md` (merged). The runtime stays the same: `!T` is `(T leaves…, i32 code)` and error names are global i32 codes.
5
+
6
+ ## User decisions
7
+ - `return error.Name`, with `fail` removed. An error is an ordinary value written `error.Name`.
8
+ - Zig-style sets: an optional named declaration `error ParseError = BadNumStr | NegativeNum`; pinned returns `ParseError!Int` or inline `error{BadNumStr}!Int`; sets merge with `||`; `!Int` stays inferred.
9
+ - Bare names in `match e` arms (`BadNumStr => …`).
10
+
11
+ ## Design
12
+ 1. **Error values.**
13
+ - `error.Name` is an expression whose type is the singleton set `error{Name}`. It needs no declaration, because names are global codes as today.
14
+ - In a `!T` or `T?` slot (return, tail, arg, field, default, list element) an error value is the failure.
15
+ - `return error.X` replaces `fail X`. In a `T?` slot `error.Missing` is simply the absent value anywhere, so the old dual rule for `fail Missing` goes away.
16
+ - `fail` is removed from the grammar, AST, checker, codegen, highlighting and LSP, with no hints.
17
+ 2. **Named sets.** `error Name = A | B | C` is a top-level item, and names must be unique, like consts. A set name is usable as a type, the type of a bare error value (`e: ParseError`), and in `X!T`.
18
+ 3. **Error-union types.**
19
+ - `!T` is inferred, as today.
20
+ - `ParseError!T` is pinned to a named set.
21
+ - `error{A, B}!T` is pinned to an inline set.
22
+ - `(A || B)!T` merges sets.
23
+ - The old `!T [A, B]` pinned spelling is removed. That also removes E2's "generics must touch the name" whitespace rule: generics go back to normal, because the set now comes before `!`.
24
+ - `T?` = `error{Missing}!T`.
25
+ 4. **`catch e`** gives `e` the exact set type. `match e` arms are bare names in the set (`error.X` is also accepted). Exhaustiveness, `e.name()`, `?`, `!`, `?.` and inference are unchanged.
26
+ 5. **Std and migration.** Every `-> !T [A, B]` pinned signature becomes `error{A, B}!T`, or a named set where one is reused, e.g. `error IoError = IoFailed | NoSuchPath` in Os.plum. Every `fail X` becomes `return error.X` (or a bare `error.X` value in a value position). The README Error handling section is rewritten; the docs, highlighting (`error` keyword, the `error.` prefix, `||` in types), VS Code grammar and LSP keywords (`error` added, `fail` removed) are updated; the editors are reinstalled.
27
+
28
+ ## Tasks
29
+ - **S1 grammar, parser and AST:** the `error` item, `error.Name` expressions, `X!T` / `error{…}!T` / `(A || B)!T` types, removal of `fail` and of the `!T [..]` spelling, and generics back to normal. Corpus, formatter and highlights.
30
+ - **S2 checker and codegen:** singleton-set error values unify into `!T`/`T?` slots; named sets resolve; `||` union; `match e` arms; set types on `catch e`. Codegen reuses the E3 code paths: an error value is just its i32 code.
31
+ - **S3 migration and docs:** std, examples, README, embedded Rust tests, LSP and editor reinstall.
32
+
33
+ Tests:
34
+ - `error_value_returns_failure` and `error_missing_is_absent_anywhere`;
35
+ - `named_set_pins` and `inline_set_pins`;
36
+ - `set_union_merge` and `pinned_rejects_extra`;
37
+ - `fail_removed` (plain parse error);
38
+ - `old_bracket_pin_removed`;
39
+ - `match_bare_names` and `match_error_prefix_accepted`;
40
+ - `generics_whitespace_free_again` (`Box [Int]`-style spacing no longer matters);
41
+ - `duplicate_error_set_name`.
42
+ - Every existing error test, migrated.