feat: tuple syntax cutover — Tuple(...) type + .(...) value
Replace the bare-paren tuple grammar with explicit, position-unambiguous
forms, mirroring how structs work:
type `(A, B)` -> `Tuple(A, B)` (named keeps `:`)
value `(a, b)` -> `.(a, b)` (named uses `=`)
typed (new) -> `Tuple(A, B).(a, b)` (like `Point.{...}`)
failable `-> (T, !)` -> `-> T !`
`-> (T1, T2, !)`-> `-> Tuple(T1, T2) !` (channel outside Tuple)
Bare `(...)` is now grouping only, everywhere; a comma in bare parens is a
hard error with a migration hint. Grouping, function types `(A, B) -> R`,
param lists, lambdas, and match bindings are unaffected.
`Tuple(...)` is strictly a TYPE in every position (including `size_of` /
`type_info` args); a tuple VALUE comes only from `.(...)` (anonymous) or
`Tuple(...).(...)` (explicitly typed). A bare `Tuple(1, 2)` is a tuple
type with non-type elements -> rejected.
The ~110 tuple-bearing corpus files were migrated with a one-shot
AST-aware migrator (the `sx migrate` tool from the prior commit, removed
here). New examples: 0130 (new syntax), 0131 (typed construction), 1060
(named-tuple failable return). 1116 golden updated for the new hint text.
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 10; // success → {n*10, 0}
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 2;
|
||||
}
|
||||
|
||||
// value-carrying `try` in a value-carrying caller — propagates {undef, tag}.
|
||||
inc :: (n: i32) -> (i32, !E) {
|
||||
inc :: (n: i32) -> i32 !E {
|
||||
v := try parse(n);
|
||||
return v + 1;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
E :: error { Bad }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 2;
|
||||
|
||||
@@ -12,37 +12,37 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, i32, !E) {
|
||||
parse :: (n: i32) -> Tuple(i32, i32) !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return (n * 2, n + 1); // success → {n*2, n+1, 0}
|
||||
return .(n * 2, n + 1); // success → {n*2, n+1, 0}
|
||||
}
|
||||
|
||||
// Multi-value `try` in a multi-value caller — propagates {undef, undef, tag}.
|
||||
inc :: (n: i32) -> (i32, i32, !E) {
|
||||
inc :: (n: i32) -> Tuple(i32, i32) !E {
|
||||
v, b := try parse(n);
|
||||
return (v + 1, b + 1);
|
||||
return .(v + 1, b + 1);
|
||||
}
|
||||
|
||||
// Multi-value `catch`, bare-expression tuple fallback (absorbs the failure).
|
||||
safe :: (n: i32) -> i32 {
|
||||
v, b := parse(n) catch (e) (40, 50);
|
||||
v, b := parse(n) catch (e) .(40, 50);
|
||||
return v + b;
|
||||
}
|
||||
|
||||
// Multi-value `catch` match-body — per-tag dispatch, each arm a value-tuple.
|
||||
classify :: (n: i32) -> i32 {
|
||||
v, b := parse(n) catch (e) == {
|
||||
case .Bad: (1, 1);
|
||||
case .Empty: (2, 2);
|
||||
else: (9, 9);
|
||||
case .Bad: .(1, 1);
|
||||
case .Empty: .(2, 2);
|
||||
else: .(9, 9);
|
||||
};
|
||||
return v + b;
|
||||
}
|
||||
|
||||
// Multi-value `or (tuple)` value-terminator (absorbs the failure).
|
||||
ortest :: (n: i32) -> i32 {
|
||||
v, b := parse(n) or (7, 8);
|
||||
v, b := parse(n) or .(7, 8);
|
||||
return v + b;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
pair :: (n: i32) -> (i32, i32, !E) {
|
||||
pair :: (n: i32) -> Tuple(i32, i32) !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return (n, n + 1);
|
||||
return .(n, n + 1);
|
||||
}
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return n * 2;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
E :: error { BadDigit, Empty, Overflow }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.BadDigit; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 2;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
ParseErr :: error { Empty, BadDigit };
|
||||
|
||||
inner :: (n: i32) -> (i32, !ParseErr) {
|
||||
inner :: (n: i32) -> i32 !ParseErr {
|
||||
if n == 0 { raise error.Empty; } // pushes a frame
|
||||
if n < 0 { raise error.BadDigit; }
|
||||
return n * 2;
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
|
||||
ParseErr :: error { Empty, BadDigit };
|
||||
|
||||
inner :: (n: i32) -> (i32, !ParseErr) {
|
||||
inner :: (n: i32) -> i32 !ParseErr {
|
||||
if n == 0 { raise error.Empty; }
|
||||
if n < 0 { raise error.BadDigit; }
|
||||
return n * 2;
|
||||
}
|
||||
|
||||
main :: () -> (i32, !ParseErr) {
|
||||
main :: () -> i32 !ParseErr {
|
||||
v := try inner(32); // succeeds → v = 64
|
||||
print("v = {}\n", v);
|
||||
return v; // success → exit code 64
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
E :: error { A, B };
|
||||
|
||||
fa :: (n: i32) -> (i32, !E) {
|
||||
fa :: (n: i32) -> i32 !E {
|
||||
if n == 0 { raise error.A; }
|
||||
if n < 0 { raise error.B; }
|
||||
return n;
|
||||
@@ -23,7 +23,7 @@ fv :: (n: i32) -> !E { // void (pure) failable
|
||||
return;
|
||||
}
|
||||
|
||||
main :: () -> (i32, !E) {
|
||||
main :: () -> i32 !E {
|
||||
onfail print("onfail fired (BUG)\n"); // must NOT fire — every chain below absorbs
|
||||
|
||||
r : i32 = 0;
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
|
||||
E :: error { A };
|
||||
|
||||
fa :: (n: i32) -> (i32, !E) {
|
||||
fa :: (n: i32) -> i32 !E {
|
||||
if n == 0 { raise error.A; }
|
||||
return n;
|
||||
}
|
||||
|
||||
main :: () -> (i32, !E) {
|
||||
main :: () -> i32 !E {
|
||||
v := try fa(0) or try fa(0) or try fa(0); // all fail → propagate to main
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
SmokeErr :: error { Empty, BadDigit, Overflow }
|
||||
|
||||
// value-carrying, named set
|
||||
sm_parse :: (n: i32) -> (i32, !SmokeErr) {
|
||||
sm_parse :: (n: i32) -> i32 !SmokeErr {
|
||||
if n < 0 { raise error.BadDigit; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
if n > 99 { raise error.Overflow; }
|
||||
@@ -23,10 +23,10 @@ sm_check :: (ok: bool) -> ! {
|
||||
}
|
||||
|
||||
// multi-value, inferred set: `try` propagates; SCC absorbs SmokeErr
|
||||
sm_pair :: (a: i32, b: i32) -> (i32, i32, !) {
|
||||
sm_pair :: (a: i32, b: i32) -> Tuple(i32, i32) ! {
|
||||
x := try sm_parse(a);
|
||||
y := try sm_parse(b);
|
||||
return (x, y);
|
||||
return .(x, y);
|
||||
}
|
||||
|
||||
// catch with a diverging block body
|
||||
@@ -38,7 +38,7 @@ sm_or_default :: (n: i32) -> i32 {
|
||||
}
|
||||
|
||||
// onfail + defer interleave: cleanup runs only on the error path
|
||||
sm_acquire :: (fail: bool) -> (i32, !) {
|
||||
sm_acquire :: (fail: bool) -> i32 ! {
|
||||
defer print(" defer A\n");
|
||||
onfail print(" onfail B\n");
|
||||
if fail { raise error.Acquire; }
|
||||
@@ -46,7 +46,7 @@ sm_acquire :: (fail: bool) -> (i32, !) {
|
||||
}
|
||||
|
||||
// or-chain: try a, fall to try b; propagate if both fail
|
||||
sm_first :: (a: i32, b: i32) -> (i32, !) {
|
||||
sm_first :: (a: i32, b: i32) -> i32 ! {
|
||||
v := try sm_parse(a) or try sm_parse(b);
|
||||
return v;
|
||||
}
|
||||
@@ -54,18 +54,18 @@ sm_first :: (a: i32, b: i32) -> (i32, !) {
|
||||
// --- Composition (ERR E5.1): failable closures, widening, generics ---
|
||||
|
||||
// Closure(...) param, try-propagated (the env is carried)
|
||||
sm_run :: (cb: Closure(i32) -> (i32, !SmokeErr), n: i32) -> (i32, !SmokeErr) {
|
||||
sm_run :: (cb: Closure(i32) -> i32 !SmokeErr, n: i32) -> i32 !SmokeErr {
|
||||
return try cb(n);
|
||||
}
|
||||
|
||||
// bare fn-type param: a NON-failable closure literal widens into the failable
|
||||
// slot (the ∅-widening adapter wraps `{value, 0}`)
|
||||
sm_widen :: (cb: (i32) -> (i32, !SmokeErr), n: i32) -> i32 {
|
||||
sm_widen :: (cb: (i32) -> i32 !SmokeErr, n: i32) -> i32 {
|
||||
return cb(n) catch (e) -1;
|
||||
}
|
||||
|
||||
// generic ($T) value-carrying failable composition, monomorphized per call
|
||||
sm_wrap :: ($T: Type, f: Closure() -> (T, !SmokeErr)) -> (T, !SmokeErr) {
|
||||
sm_wrap :: ($T: Type, f: Closure() -> T !SmokeErr) -> T !SmokeErr {
|
||||
return try f();
|
||||
}
|
||||
|
||||
@@ -105,9 +105,9 @@ main :: () {
|
||||
if !gerr { print("or-chain: {}\n", g); }
|
||||
|
||||
// multi-value failable consumed by catch (tuple body)
|
||||
p, q := sm_pair(0, 3) catch (e) (0, 0);
|
||||
p, q := sm_pair(0, 3) catch (e) .(0, 0);
|
||||
print("pair-catch: {} {}\n", p, q);
|
||||
p2, q2 := sm_pair(4, 5) catch (e) (0, 0);
|
||||
p2, q2 := sm_pair(4, 5) catch (e) .(0, 0);
|
||||
print("pair-ok: {} {}\n", p2, q2);
|
||||
|
||||
// pure failable: absorb with no-binding catch
|
||||
@@ -120,15 +120,15 @@ main :: () {
|
||||
iv, ierr := sm_acquire(false);
|
||||
|
||||
// composition: inline failable closure literal through a Closure(...) param
|
||||
cl := sm_run(closure((x: i32) -> (i32, !SmokeErr) { if x < 0 { raise error.BadDigit; } return x * 2; }), 6) catch (e) -1;
|
||||
cl := sm_run(closure((x: i32) -> i32 !SmokeErr { if x < 0 { raise error.BadDigit; } return x * 2; }), 6) catch (e) -1;
|
||||
print("closure-run: {}\n", cl); // 12
|
||||
print("closure-run-err: {}\n", sm_run(closure((x: i32) -> (i32, !SmokeErr) { raise error.Empty; }), 1) catch (e) -9); // -9
|
||||
print("closure-run-err: {}\n", sm_run(closure((x: i32) -> i32 !SmokeErr { raise error.Empty; }), 1) catch (e) -9); // -9
|
||||
|
||||
// non-failable closure literal widened into the failable bare slot
|
||||
print("widen: {}\n", sm_widen(closure((x: i32) -> i32 => x + 1), 9)); // 10
|
||||
|
||||
// generic failable composition (monomorphized at i32)
|
||||
print("wrap: {}\n", sm_wrap(i32, closure(() -> (i32, !SmokeErr) { return 42; })) catch (e) 0); // 42
|
||||
print("wrap: {}\n", sm_wrap(i32, closure(() -> i32 !SmokeErr { return 42; })) catch (e) 0); // 42
|
||||
|
||||
print("errors ok\n");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 2;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 2;
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
|
||||
E :: error { Neg }
|
||||
|
||||
runwith :: (cb: Closure(i64) -> (i64, !E), n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||
runwith :: (cb: Closure(i64) -> i64 !E, n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||
|
||||
main :: () -> i32 {
|
||||
// block-body and arrow-body failable closures, called directly
|
||||
m := closure((x: i64) -> (i64, !E) { if x < 0 { raise error.Neg; } return x * 2; });
|
||||
n := closure((x: i64) -> (i64, !E) => x + 1);
|
||||
m := closure((x: i64) -> i64 !E { if x < 0 { raise error.Neg; } return x * 2; });
|
||||
n := closure((x: i64) -> i64 !E => x + 1);
|
||||
print("{} {} {} {}\n", m(5) catch (e) 0, m(-1) catch (e) 99, m(-1) or 7, n(40) catch (e) 0); // 10 99 7 41
|
||||
|
||||
// failable closure passed as a Closure(...) parameter
|
||||
|
||||
@@ -12,21 +12,21 @@
|
||||
|
||||
E :: error { Neg }
|
||||
|
||||
bare :: (cb: (i64) -> (i64, !E), n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||
chain :: (cb: Closure(i64) -> (i64, !E), n: i64) -> (i64, !E) { return try cb(n); }
|
||||
bare :: (cb: (i64) -> i64 !E, n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||
chain :: (cb: Closure(i64) -> i64 !E, n: i64) -> i64 !E { return try cb(n); }
|
||||
|
||||
dbl :: (x: i64) -> (i64, !E) { if x < 0 { raise error.Neg; } return x * 2; }
|
||||
dbl :: (x: i64) -> i64 !E { if x < 0 { raise error.Neg; } return x * 2; }
|
||||
|
||||
main :: () -> i32 {
|
||||
// failable closure literal through a bare fn-type param (matching ABI)
|
||||
print("bare ok={} err={}\n",
|
||||
bare(closure((x: i64) -> (i64, !E) { if x < 0 { raise error.Neg; } return x * 2; }), 5),
|
||||
bare(closure((x: i64) -> (i64, !E) => x * 2), -1)); // ok=10; err: arrow never raises → cb(-1) = -2
|
||||
bare(closure((x: i64) -> i64 !E { if x < 0 { raise error.Neg; } return x * 2; }), 5),
|
||||
bare(closure((x: i64) -> i64 !E => x * 2), -1)); // ok=10; err: arrow never raises → cb(-1) = -2
|
||||
|
||||
// Closure(...) param, try-propagated, then caught at the call site
|
||||
print("chain ok={} err={}\n",
|
||||
chain(closure((x: i64) -> (i64, !E) => x + 6), 4) catch (e) 0, // 10
|
||||
chain(closure((x: i64) -> (i64, !E) { raise error.Neg; }), 1) catch (e) 0); // 0
|
||||
chain(closure((x: i64) -> i64 !E => x + 6), 4) catch (e) 0, // 10
|
||||
chain(closure((x: i64) -> i64 !E { raise error.Neg; }), 1) catch (e) 0); // 0
|
||||
|
||||
// NON-failable closure literal widened into the failable bare slot
|
||||
print("widen={}\n", bare(closure((x: i64) -> i64 => x + 1), 9)); // 10
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
All :: error { Negative, Other }
|
||||
|
||||
// `h` is a bare-`!` Closure slot; the caller declares the union as `!All`.
|
||||
dispatch :: (h: Closure(i32) -> (i32, !), x: i32) -> (i32, !All) {
|
||||
dispatch :: (h: Closure(i32) -> i32 !, x: i32) -> i32 !All {
|
||||
return try h(x);
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ main :: () -> i32 {
|
||||
push Context.{ allocator = xx gpa } {
|
||||
// Two literals of the SAME shape raising DIFFERENT tags both feed the
|
||||
// one shared `Closure(i32)->(i32,!)` union node.
|
||||
handlers : List(Closure(i32) -> (i32, !)) = .{};
|
||||
handlers.append(closure((x: i32) -> (i32, !) { if x < 0 { raise error.Negative; } return x * 2; }));
|
||||
handlers.append(closure((x: i32) -> (i32, !) { if x == 0 { raise error.Other; } return x + 100; }));
|
||||
handlers : List(Closure(i32) -> i32 !) = .{};
|
||||
handlers.append(closure((x: i32) -> i32 ! { if x < 0 { raise error.Negative; } return x * 2; }));
|
||||
handlers.append(closure((x: i32) -> i32 ! { if x == 0 { raise error.Other; } return x + 100; }));
|
||||
|
||||
// success paths
|
||||
print("ok0={}\n", dispatch(handlers.items[0], 5) catch (e) 0); // 10
|
||||
|
||||
@@ -9,16 +9,16 @@
|
||||
|
||||
Small :: error { Unrelated }
|
||||
|
||||
reject :: (h: Closure(i32) -> (i32, !), x: i32) -> (i32, !Small) {
|
||||
reject :: (h: Closure(i32) -> i32 !, x: i32) -> i32 !Small {
|
||||
return try h(x); // Negative, Other ∉ Small → two diagnostics
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
gpa := GPA.init();
|
||||
push Context.{ allocator = xx gpa } {
|
||||
handlers : List(Closure(i32) -> (i32, !)) = .{};
|
||||
handlers.append(closure((x: i32) -> (i32, !) { if x < 0 { raise error.Negative; } return x; }));
|
||||
handlers.append(closure((x: i32) -> (i32, !) { if x == 0 { raise error.Other; } return x; }));
|
||||
handlers : List(Closure(i32) -> i32 !) = .{};
|
||||
handlers.append(closure((x: i32) -> i32 ! { if x < 0 { raise error.Negative; } return x; }));
|
||||
handlers.append(closure((x: i32) -> i32 ! { if x == 0 { raise error.Other; } return x; }));
|
||||
print("r={}\n", reject(handlers.items[0], 5) catch (e) 0);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
E :: error { Neg }
|
||||
|
||||
take :: (cb: Closure(i32) -> (i32, !E), x: i32) -> i32 { return cb(x) catch (e) -1; }
|
||||
take :: (cb: Closure(i32) -> i32 !E, x: i32) -> i32 { return cb(x) catch (e) -1; }
|
||||
|
||||
main :: () -> i32 {
|
||||
// `-> i32` (non-failable) but the body raises → lambda-specific hint:
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
|
||||
E :: error { Bad }
|
||||
|
||||
wrap :: ($T: Type, f: Closure() -> (T, !E)) -> (T, !E) { return try f(); }
|
||||
wrap :: ($T: Type, f: Closure() -> T !E) -> T !E { return try f(); }
|
||||
|
||||
main :: () -> i32 {
|
||||
// success, consumed by catch
|
||||
print("catch={}\n", wrap(i32, closure(() -> (i32, !E) { return 7; })) catch (e) -1); // 7
|
||||
print("catch={}\n", wrap(i32, closure(() -> i32 !E { return 7; })) catch (e) -1); // 7
|
||||
|
||||
// success, consumed by destructure (binds value + error slot); the value
|
||||
// slot is read only under an `if !err` guard (ERR E1.8 path-sensitivity)
|
||||
r, err := wrap(i32, closure(() -> (i32, !E) { return 9; }));
|
||||
r, err := wrap(i32, closure(() -> i32 !E { return 9; }));
|
||||
if !err { print("destr={} ok=true\n", r); } // destr=9 ok=true
|
||||
|
||||
// failure path: the raised tag propagates through the generic `try`
|
||||
print("fail={}\n", wrap(i32, closure(() -> (i32, !E) { raise error.Bad; }) ) catch (e) -1); // -1
|
||||
print("fail={}\n", wrap(i32, closure(() -> i32 !E { raise error.Bad; }) ) catch (e) -1); // -1
|
||||
|
||||
// a second monomorphization at a different T
|
||||
print("u8={}\n", wrap(u8, closure(() -> (u8, !E) { return 200; })) catch (e) 0); // 200
|
||||
print("u8={}\n", wrap(u8, closure(() -> u8 !E { return 200; })) catch (e) 0); // 200
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
E :: error { Z }
|
||||
|
||||
bare :: (cb: (i64) -> i64, n: i64) -> i64 { return cb(n); }
|
||||
baref :: (cb: (i64) -> (i64, !E), n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||
baref :: (cb: (i64) -> i64 !E, n: i64) -> i64 { return cb(n) catch (e) -1; }
|
||||
|
||||
main :: () -> i32 {
|
||||
inc := closure((x: i64) -> i64 => x + 1); // capture-free closure var
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 10;
|
||||
@@ -29,7 +29,7 @@ guarded :: (n: i32) -> i32 {
|
||||
}
|
||||
|
||||
// `if err { raise }` in a failable function: same fall-through proof.
|
||||
relay :: (n: i32) -> (i32, !E) {
|
||||
relay :: (n: i32) -> i32 !E {
|
||||
v, err := parse(n);
|
||||
if err { raise err; }
|
||||
return v + 1; // err proven absent here
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
E :: error { Bad }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return n * 10;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
E :: error { Bad }
|
||||
|
||||
failing :: () -> !E { raise error.Bad; }
|
||||
recover :: () -> (i32, !E) { raise error.Bad; }
|
||||
recover :: () -> i32 !E { raise error.Bad; }
|
||||
|
||||
work :: (n: i32) -> !E {
|
||||
defer print("defer: always\n"); // plain cleanup
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
E :: error { Bad }
|
||||
|
||||
probe :: () -> (i32, !E) { return 21; }
|
||||
probe :: () -> i32 !E { return 21; }
|
||||
failing :: () -> !E { raise error.Bad; }
|
||||
|
||||
run :: () {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
E :: error { Bad }
|
||||
|
||||
failing :: () -> !E { raise error.Bad; }
|
||||
recover :: () -> (i32, !E) { return 21; }
|
||||
recover :: () -> i32 !E { return 21; }
|
||||
|
||||
work :: () {
|
||||
defer {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
E :: error { Bad }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return n * 10;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
E :: error { Bad, Empty }
|
||||
|
||||
parse :: (n: i32) -> (i32, !E) {
|
||||
parse :: (n: i32) -> i32 !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
if n == 0 { raise error.Empty; }
|
||||
return n * 2;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
Color :: enum { red; green; blue; }
|
||||
E :: error { Nope }
|
||||
|
||||
pick :: (s: string) -> (Color, !E) {
|
||||
pick :: (s: string) -> Color !E {
|
||||
if s == "red" { return .red; }
|
||||
if s == "blue" { return .blue; } // non-zero ordinal (2)
|
||||
raise error.Nope;
|
||||
|
||||
@@ -22,13 +22,13 @@ Color :: enum { red; green; blue; }
|
||||
E :: error { Nope }
|
||||
|
||||
// F1: bare-value success path AND explicit-tuple error path in one function.
|
||||
classify :: (s: string) -> (Color, !E) {
|
||||
classify :: (s: string) -> Color !E {
|
||||
if s == "ok" { return .blue; } // bare value → {2, 0}
|
||||
return (.red, error.Nope); // explicit full tuple → {0, 1}
|
||||
return .(.red, error.Nope); // explicit full tuple → {0, 1}
|
||||
}
|
||||
|
||||
// F2: comptime parameter forces inline lowering of the body.
|
||||
ct_pick :: ($n: i32, s: string) -> (Color, !E) {
|
||||
ct_pick :: ($n: i32, s: string) -> Color !E {
|
||||
if s == "red" { return .red; } // bare value, inline path → {0, 0}
|
||||
if s == "blue" { return .blue; } // bare value, inline path → {2, 0}
|
||||
raise error.Nope; // inline error path → {undef, 1}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
E :: error { Boom }
|
||||
|
||||
f :: (fail: bool) -> (i64, !E) {
|
||||
f :: (fail: bool) -> i64 !E {
|
||||
if fail { raise error.Boom; }
|
||||
return 42;
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ LE :: error { Bad }
|
||||
Box :: struct ($R: Type) { v: R; }
|
||||
|
||||
// Returns `($R, !LE)` — a value-failable. `$R` is inferred from the arg.
|
||||
get :: ufcs (b: *Box($R)) -> ($R, !LE) { return b.v; }
|
||||
get :: ufcs (b: *Box($R)) -> $R !LE { return b.v; }
|
||||
|
||||
26
examples/errors/1060-errors-named-tuple-failable.sx
Normal file
26
examples/errors/1060-errors-named-tuple-failable.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
// A failable function returning a NAMED tuple value `-> Tuple(x: A, y: B) !E`
|
||||
// flattens its value fields into the result tuple (`{ x: A, y: B, err }`),
|
||||
// keeping the `.x`/`.y` names addressable on both the success value and the
|
||||
// `or` fallback. Exercises the success path, a `raise` path, and an
|
||||
// `or .(x=.., y=..)` terminator.
|
||||
//
|
||||
// Regression (issue 0179-adjacent named-tuple-failable miscompile): a named
|
||||
// failable tuple used to WRAP as `{ {A,B}, err }` while the value-return
|
||||
// lowering inserted the value slots FLAT, producing invalid LLVM
|
||||
// (`Invalid InsertValueInst operands`).
|
||||
#import "modules/std.sx";
|
||||
|
||||
E :: error { Bad }
|
||||
|
||||
two :: (n: i64) -> Tuple(x: i64, y: i64) !E {
|
||||
if n < 0 { raise error.Bad; }
|
||||
return .(x = n, y = n + 1);
|
||||
}
|
||||
|
||||
main :: () {
|
||||
ok := two(5) or .(x = 0, y = 0);
|
||||
print("{} {}\n", ok.x, ok.y);
|
||||
|
||||
bad := two(-1) or .(x = 0, y = 0);
|
||||
print("{} {}\n", bad.x, bad.y);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
5 6
|
||||
0 0
|
||||
Reference in New Issue
Block a user