ERR/E2.1a: value-carrying failable producer (return value + raise → tuple ABI)

The producer side of the error-channel tuple ABI for value-carrying `-> (T, !)`
functions. A failable that returns a value OR an error now lowers correctly;
the result is consumed via destructure (`v, err := f()`). Single-value
`-> (T, !)`; multi-value `-> (T1, T2, !)` and the value-carrying try/catch
consumers (E2.1b) follow.

- lowerReturn: a value-carrying failable's `return v;` assembles the success
  tuple `{v, 0}` (compiler appends the no-error slot) via lowerFailableSuccessReturn
  (tuple_init). Forwarding a full failable tuple (`return other_failable()` /
  explicit `return (v, e)`) returns as-is. Multi-value returns bail loudly (E2).
- lowerRaise: the value-carrying branch (previously a loud bail) now builds
  `{undef value slots..., tag}` (constUndef per value slot + the error tag) and
  returns it — any arity.
- helpers: buildFailableTuple (tuple_init from value refs + tag) + emitTupleRet
  (return honoring inline-comptime targets).

Value-carrying `try` / `catch` still bail (E2.1b). Tests:
examples/228-value-failable.sx (return value + both raises, consumed by
destructure; exit 60). Gates: zig build, zig build test, 266/266 examples.
This commit is contained in:
agra
2026-05-31 21:42:51 +03:00
parent 0bbff9d7fb
commit 17c19d5d30
4 changed files with 98 additions and 4 deletions

View File

@@ -0,0 +1,35 @@
// Value-carrying failable functions (ERR step E2.1a — the producer side of the
// error-channel tuple ABI). A `-> (T, !E)` function returns EITHER a value OR
// an error: `return v;` yields the success tuple `{v, 0}` (the compiler appends
// the no-error slot), and `raise error.X` yields `{undef, tag}` (value slot
// undefined, error slot = the tag). Today the result is consumed by
// destructuring `v, err := f()` (which extracts both slots); the value-carrying
// `try` / `catch` consumers land in E2.1b.
#import "modules/std.sx";
E :: error { Bad, Empty }
parse :: (n: s32) -> (s32, !E) {
if n < 0 { raise error.Bad; }
if n == 0 { raise error.Empty; }
return n * 10; // success → {n*10, 0}
}
main :: () -> s32 {
r : s32 = 0;
v1, e1 := parse(5); // success → v1 = 50, e1 = no error
if e1 == error.Bad { r = r + 1000; } // false
r = r + v1; // +50
v2, e2 := parse(-1); // Bad
if e2 == error.Bad { r = r + 7; } // true → +7
if e2 == error.Empty { r = r + 200; } // false
v3, e3 := parse(0); // Empty
if e3 == error.Empty { r = r + 3; } // true → +3
print("value-failable result: {}\n", r); // 50 + 7 + 3 = 60
return r;
}