style: migrate arrow-block lambdas () => { .. } to () { .. }
The canonical sx block-body lambda is `(params) { stmts }` (and
`(params) -> Ret { stmts }`); the arrow form `=>` is for EXPRESSION bodies
(`(params) => expr`). The arrow-block hybrid `(params) => { .. }` was being
used in 33 files — convert all of them by dropping the `=>`. The two forms are
exactly equivalent (verified: identical IR and identical runtime values — the
block tail is the value with or without a `-> Ret`), so this is a pure source
cleanup: no `.ir` churn, and the only snapshot change is 0923's diagnostic
COLUMN (a negative narrowing test whose error span shifted by the removed `=> `).
Arrow EXPRESSION bodies (`=> expr`, `=> .{..}`, `=> [..]`) and `=>` inside
comments/strings were left untouched. Migrated across examples/concurrency,
examples/{closures,ffi-objc,generics,optionals,types}, issues/, and the stdlib
(io.sx, sched.sx). Suite 855/0.
This commit is contained in:
@@ -10,7 +10,7 @@ Ctx :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
c : Ctx = .{ on = (f: Fmt) => {
|
c : Ctx = .{ on = (f: Fmt) {
|
||||||
n : i64 = xx f;
|
n : i64 = xx f;
|
||||||
print("cl f = {}\n", n);
|
print("cl f = {}\n", n);
|
||||||
}};
|
}};
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ ticks : i32 = 0;
|
|||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
h : Holder = .{};
|
h : Holder = .{};
|
||||||
h.set(() => { ticks += 1; });
|
h.set(() { ticks += 1; });
|
||||||
|
|
||||||
h.call_direct();
|
h.call_direct();
|
||||||
h.call_hoisted();
|
h.call_hoisted();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
main :: () {
|
main :: () {
|
||||||
pick := (p: ?i64) -> i64 => {
|
pick := (p: ?i64) -> i64 {
|
||||||
if p == null { return -1; }
|
if p == null { return -1; }
|
||||||
return p; // narrowed inside the lambda body
|
return p; // narrowed inside the lambda body
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
// the call types as `unresolved` (so `catch`/`try` reject it).
|
// the call types as `unresolved` (so `catch`/`try` reject it).
|
||||||
//
|
//
|
||||||
// Regression (PLAN-IO-UNIFY Phase 3 blocker): the async completion closure
|
// Regression (PLAN-IO-UNIFY Phase 3 blocker): the async completion closure
|
||||||
// `() => { f.value = worker() catch {…} }` captures a `Closure() -> ($R, !)`
|
// `() { f.value = worker() catch {…} }` captures a `Closure() -> ($R, !)`
|
||||||
// worker and consumes its error channel — exactly this shape.
|
// worker and consumes its error channel — exactly this shape.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Box :: struct { run: Closure() -> void; }
|
|||||||
// `catch` path: the nested closure absorbs the worker's error.
|
// `catch` path: the nested closure absorbs the worker's error.
|
||||||
run_catch :: (worker: Closure() -> (i64, !)) {
|
run_catch :: (worker: Closure() -> (i64, !)) {
|
||||||
b : Box = ---;
|
b : Box = ---;
|
||||||
b.run = () => {
|
b.run = () {
|
||||||
v := worker() catch {
|
v := worker() catch {
|
||||||
print("caught\n");
|
print("caught\n");
|
||||||
return;
|
return;
|
||||||
@@ -27,17 +27,17 @@ run_catch :: (worker: Closure() -> (i64, !)) {
|
|||||||
|
|
||||||
// `try` path: the nested closure is itself failable and propagates.
|
// `try` path: the nested closure is itself failable and propagates.
|
||||||
mk_trier :: (worker: Closure() -> (i64, !)) -> Closure() -> (i64, !) {
|
mk_trier :: (worker: Closure() -> (i64, !)) -> Closure() -> (i64, !) {
|
||||||
return () -> (i64, !) => {
|
return () -> (i64, !) {
|
||||||
v := try worker();
|
v := try worker();
|
||||||
v + 100
|
v + 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> i64 {
|
main :: () -> i64 {
|
||||||
run_catch(() -> (i64, !) => { 7 }); // ok 7
|
run_catch(() -> (i64, !) { 7 }); // ok 7
|
||||||
run_catch(() -> (i64, !) => { raise error.Bad; }); // caught
|
run_catch(() -> (i64, !) { raise error.Bad; }); // caught
|
||||||
|
|
||||||
t := mk_trier(() -> (i64, !) => { 5 });
|
t := mk_trier(() -> (i64, !) { 5 });
|
||||||
r := t() catch { return 1; };
|
r := t() catch { return 1; };
|
||||||
print("try {}\n", r); // try 105
|
print("try {}\n", r); // try 105
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ main :: () -> i64 {
|
|||||||
b : CB = ---;
|
b : CB = ---;
|
||||||
b.add = (x: i64, y: i64) => x + y;
|
b.add = (x: i64, y: i64) => x + y;
|
||||||
b.fp = triple;
|
b.fp = triple;
|
||||||
b.work = (n: i64) -> (i64, !) => {
|
b.work = (n: i64) -> (i64, !) {
|
||||||
if n < 0 { raise error.Negative; }
|
if n < 0 { raise error.Negative; }
|
||||||
n * 10
|
n * 10
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ main :: () -> i64 {
|
|||||||
// Three DIFFERENT fiber bodies (distinct captured ids), interleaving via
|
// Three DIFFERENT fiber bodies (distinct captured ids), interleaving via
|
||||||
// yield_now. Each appends its id once per round for 3 rounds.
|
// yield_now. Each appends its id once per round for 3 rounds.
|
||||||
spawn_worker :: (ps: *sched.Scheduler, psh: *Shared, my_id: i64) {
|
spawn_worker :: (ps: *sched.Scheduler, psh: *Shared, my_id: i64) {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
r := 0;
|
r := 0;
|
||||||
while r < 3 {
|
while r < 3 {
|
||||||
append(psh, my_id);
|
append(psh, my_id);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ main :: () -> i64 {
|
|||||||
// Fiber A: record 10, park, then (after wake) record 11. Store A's handle in
|
// Fiber A: record 10, park, then (after wake) record 11. Store A's handle in
|
||||||
// the shared state so B can wake it.
|
// the shared state so B can wake it.
|
||||||
mk_a :: (ps: *sched.Scheduler, psh: *Sh) {
|
mk_a :: (ps: *sched.Scheduler, psh: *Sh) {
|
||||||
psh.parked = ps.spawn(() => {
|
psh.parked = ps.spawn(() {
|
||||||
rec(psh, 10);
|
rec(psh, 10);
|
||||||
ps.suspend_self();
|
ps.suspend_self();
|
||||||
rec(psh, 11);
|
rec(psh, 11);
|
||||||
@@ -45,7 +45,7 @@ main :: () -> i64 {
|
|||||||
// Fiber B: record 20, wake A (legit) + a spurious second wake (safe no-op),
|
// Fiber B: record 20, wake A (legit) + a spurious second wake (safe no-op),
|
||||||
// record 21.
|
// record 21.
|
||||||
mk_b :: (ps: *sched.Scheduler, psh: *Sh) {
|
mk_b :: (ps: *sched.Scheduler, psh: *Sh) {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
rec(psh, 20);
|
rec(psh, 20);
|
||||||
ps.wake(psh.parked); // legitimate: A is parked
|
ps.wake(psh.parked); // legitimate: A is parked
|
||||||
ps.wake(psh.parked); // spurious: A is now .ready/queued — must no-op
|
ps.wake(psh.parked); // spurious: A is now .ready/queued — must no-op
|
||||||
|
|||||||
@@ -43,16 +43,16 @@ main :: () -> i64 {
|
|||||||
// cancel. It runs as a fiber so `await` has a `self.current` to park. The
|
// cancel. It runs as a fiber so `await` has a `self.current` to park. The
|
||||||
// scheduler is installed as `context.io`, so the unified async layer reaches it.
|
// scheduler is installed as `context.io`, so the unified async layer reaches it.
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
// Worker A yields mid-body so B interleaves before A completes.
|
// Worker A yields mid-body so B interleaves before A completes.
|
||||||
a := context.io.async(() -> (i64, !) => {
|
a := context.io.async(() -> (i64, !) {
|
||||||
rec(pl, 1);
|
rec(pl, 1);
|
||||||
ps.yield_now(); // suspend A; B (already spawned) runs to completion
|
ps.yield_now(); // suspend A; B (already spawned) runs to completion
|
||||||
rec(pl, 3);
|
rec(pl, 3);
|
||||||
42
|
42
|
||||||
});
|
});
|
||||||
// Worker B runs straight through (no yield).
|
// Worker B runs straight through (no yield).
|
||||||
b := context.io.async(() -> (i64, !) => {
|
b := context.io.async(() -> (i64, !) {
|
||||||
rec(pl, 2);
|
rec(pl, 2);
|
||||||
100
|
100
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ main :: () -> i64 {
|
|||||||
pl := @lg;
|
pl := @lg;
|
||||||
|
|
||||||
// Spawn order A, B, C, D, E — but the WAKE order is set by deadline.
|
// Spawn order A, B, C, D, E — but the WAKE order is set by deadline.
|
||||||
ps.spawn(() => { ps.sleep(30); rec(pl, 1, ps.now_ms()); }); // A: latest
|
ps.spawn(() { ps.sleep(30); rec(pl, 1, ps.now_ms()); }); // A: latest
|
||||||
ps.spawn(() => { ps.sleep(10); rec(pl, 2, ps.now_ms()); }); // B: earliest
|
ps.spawn(() { ps.sleep(10); rec(pl, 2, ps.now_ms()); }); // B: earliest
|
||||||
ps.spawn(() => { ps.sleep(20); rec(pl, 3, ps.now_ms()); }); // C: middle
|
ps.spawn(() { ps.sleep(20); rec(pl, 3, ps.now_ms()); }); // C: middle
|
||||||
// Same-deadline FIFO pair: D before E, both at t=15 → wake D then E.
|
// Same-deadline FIFO pair: D before E, both at t=15 → wake D then E.
|
||||||
ps.spawn(() => { ps.sleep(15); rec(pl, 4, ps.now_ms()); }); // D
|
ps.spawn(() { ps.sleep(15); rec(pl, 4, ps.now_ms()); }); // D
|
||||||
ps.spawn(() => { ps.sleep(15); rec(pl, 5, ps.now_ms()); }); // E
|
ps.spawn(() { ps.sleep(15); rec(pl, 5, ps.now_ms()); }); // E
|
||||||
|
|
||||||
s.run();
|
s.run();
|
||||||
|
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ main :: () -> i64 {
|
|||||||
|
|
||||||
// Sleeper: arm sleep(100), park; when woken (early), record 1 and finish.
|
// Sleeper: arm sleep(100), park; when woken (early), record 1 and finish.
|
||||||
mk_sleeper :: (ps: *sched.Scheduler, pst: *S) {
|
mk_sleeper :: (ps: *sched.Scheduler, pst: *S) {
|
||||||
pst.sleeper = ps.spawn(() => { ps.sleep(100); rec(pst, 1); });
|
pst.sleeper = ps.spawn(() { ps.sleep(100); rec(pst, 1); });
|
||||||
}
|
}
|
||||||
// Waker: record 2, then wake the sleeper BEFORE its 100ms timer fires.
|
// Waker: record 2, then wake the sleeper BEFORE its 100ms timer fires.
|
||||||
mk_waker :: (ps: *sched.Scheduler, pst: *S) {
|
mk_waker :: (ps: *sched.Scheduler, pst: *S) {
|
||||||
ps.spawn(() => { rec(pst, 2); ps.wake(pst.sleeper); });
|
ps.spawn(() { rec(pst, 2); ps.wake(pst.sleeper); });
|
||||||
}
|
}
|
||||||
mk_sleeper(ps, pst);
|
mk_sleeper(ps, pst);
|
||||||
mk_waker(ps, pst);
|
mk_waker(ps, pst);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ main :: () -> i64 {
|
|||||||
|
|
||||||
// Reader: block on the (empty) pipe until it is readable, then read 3 bytes.
|
// Reader: block on the (empty) pipe until it is readable, then read 3 bytes.
|
||||||
mk_reader :: (ps: *sched.Scheduler, pst: *S, rfd: i32) {
|
mk_reader :: (ps: *sched.Scheduler, pst: *S, rfd: i32) {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
ps.block_on_fd(rfd, true); // parks until read_fd is readable
|
ps.block_on_fd(rfd, true); // parks until read_fd is readable
|
||||||
n := read(rfd, xx @pst.bytes[0], xx 3);
|
n := read(rfd, xx @pst.bytes[0], xx 3);
|
||||||
pst.read_n = xx n;
|
pst.read_n = xx n;
|
||||||
@@ -65,7 +65,7 @@ main :: () -> i64 {
|
|||||||
}
|
}
|
||||||
// Writer: write 3 bytes ('a','b','c') to the write end.
|
// Writer: write 3 bytes ('a','b','c') to the write end.
|
||||||
mk_writer :: (ps: *sched.Scheduler, pst: *S, wfd: i32) {
|
mk_writer :: (ps: *sched.Scheduler, pst: *S, wfd: i32) {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
buf : [3]u8 = ---;
|
buf : [3]u8 = ---;
|
||||||
buf[0] = xx 97; buf[1] = xx 98; buf[2] = xx 99; // 'a' 'b' 'c'
|
buf[0] = xx 97; buf[1] = xx 98; buf[2] = xx 99; // 'a' 'b' 'c'
|
||||||
write(wfd, xx @buf[0], xx 3);
|
write(wfd, xx @buf[0], xx 3);
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ main :: () -> i64 {
|
|||||||
// scheduler is installed as `context.io`, so the unified async layer
|
// scheduler is installed as `context.io`, so the unified async layer
|
||||||
// (`context.io.async`/`await`/`sleep`/`now_ms`) reaches it inside the workers.
|
// (`context.io.async`/`await`/`sleep`/`now_ms`) reaches it inside the workers.
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
// Launch three async workers; each sleeps, logs its completion, returns.
|
// Launch three async workers; each sleeps, logs its completion, returns.
|
||||||
a := context.io.async(() -> (i64, !) => { try context.io.sleep(30); rec(pl, 1, context.io.now_ms()); 100 });
|
a := context.io.async(() -> (i64, !) { try context.io.sleep(30); rec(pl, 1, context.io.now_ms()); 100 });
|
||||||
b := context.io.async(() -> (i64, !) => { try context.io.sleep(10); rec(pl, 2, context.io.now_ms()); 20 });
|
b := context.io.async(() -> (i64, !) { try context.io.sleep(10); rec(pl, 2, context.io.now_ms()); 20 });
|
||||||
c := context.io.async(() -> (i64, !) => { try context.io.sleep(20); rec(pl, 3, context.io.now_ms()); 3 });
|
c := context.io.async(() -> (i64, !) { try context.io.sleep(20); rec(pl, 3, context.io.now_ms()); 3 });
|
||||||
|
|
||||||
// Await in SPAWN order; results come back correct regardless.
|
// Await in SPAWN order; results come back correct regardless.
|
||||||
va := a.await() or { -1 };
|
va := a.await() or { -1 };
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
sched :: #import "modules/std/sched.sx";
|
sched :: #import "modules/std/sched.sx";
|
||||||
main :: () -> i64 {
|
main :: () -> i64 {
|
||||||
s := sched.Scheduler.init(); ps := @s;
|
s := sched.Scheduler.init(); ps := @s;
|
||||||
ps.spawn(() => { ps.sleep(10); ps.sleep(-5); }); // -5 → loud abort
|
ps.spawn(() { ps.sleep(10); ps.sleep(-5); }); // -5 → loud abort
|
||||||
s.run();
|
s.run();
|
||||||
print("unreachable\n");
|
print("unreachable\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ S :: struct { t: *Future(i64); }
|
|||||||
main :: () -> i64 {
|
main :: () -> i64 {
|
||||||
st : S = ---; st.t = null;
|
st : S = ---; st.t = null;
|
||||||
s := sched.Scheduler.init(); ps := @s; pst := @st;
|
s := sched.Scheduler.init(); ps := @s; pst := @st;
|
||||||
mkprod :: (ps: *sched.Scheduler, pst: *S) { pst.t = context.io.async(() -> (i64, !) => { ps.yield_now(); 42 }); }
|
mkprod :: (ps: *sched.Scheduler, pst: *S) { pst.t = context.io.async(() -> (i64, !) { ps.yield_now(); 42 }); }
|
||||||
mkw :: (ps: *sched.Scheduler, pst: *S) { ps.spawn(() => { x := pst.t.await() or { -1 }; print("got {}\n", x); }); }
|
mkw :: (ps: *sched.Scheduler, pst: *S) { ps.spawn(() { x := pst.t.await() or { -1 }; print("got {}\n", x); }); }
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
mkprod(ps, pst); mkw(ps, pst); mkw(ps, pst); // second waiter → loud abort
|
mkprod(ps, pst); mkw(ps, pst); mkw(ps, pst); // second waiter → loud abort
|
||||||
s.run();
|
s.run();
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ main :: () -> i64 {
|
|||||||
ps := @s; pst := @st;
|
ps := @s; pst := @st;
|
||||||
|
|
||||||
// SLEEPER — arms a virtual-time timer, then parks.
|
// SLEEPER — arms a virtual-time timer, then parks.
|
||||||
ps.spawn(() => { ps.sleep(5); });
|
ps.spawn(() { ps.sleep(5); });
|
||||||
|
|
||||||
// READER — blocks on the empty pipe until kqueue reports it readable.
|
// READER — blocks on the empty pipe until kqueue reports it readable.
|
||||||
mk_reader :: (ps: *sched.Scheduler, pst: *S, rfd: i32) {
|
mk_reader :: (ps: *sched.Scheduler, pst: *S, rfd: i32) {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
ps.block_on_fd(rfd, true);
|
ps.block_on_fd(rfd, true);
|
||||||
n := read(rfd, xx @pst.bytes[0], xx 3);
|
n := read(rfd, xx @pst.bytes[0], xx 3);
|
||||||
pst.read_n = xx n;
|
pst.read_n = xx n;
|
||||||
@@ -83,7 +83,7 @@ main :: () -> i64 {
|
|||||||
}
|
}
|
||||||
// WRITER — writes 'a' 'b' 'c', making the pipe readable.
|
// WRITER — writes 'a' 'b' 'c', making the pipe readable.
|
||||||
mk_writer :: (ps: *sched.Scheduler, wfd: i32) {
|
mk_writer :: (ps: *sched.Scheduler, wfd: i32) {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
buf : [3]u8 = ---;
|
buf : [3]u8 = ---;
|
||||||
buf[0] = xx 97; buf[1] = xx 98; buf[2] = xx 99;
|
buf[0] = xx 97; buf[1] = xx 98; buf[2] = xx 99;
|
||||||
write(wfd, xx @buf[0], xx 3);
|
write(wfd, xx @buf[0], xx 3);
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ main :: () -> i64 {
|
|||||||
|
|
||||||
// The coordinator runs as a fiber so `race` has a `current` to park.
|
// The coordinator runs as a fiber so `race` has a `current` to park.
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
// Three async workers, DIFFERENT result types and sleep durations.
|
// Three async workers, DIFFERENT result types and sleep durations.
|
||||||
a := context.io.async(() -> (i64, !) => { try context.io.sleep(10); rec(pl, 1, context.io.now_ms()); 111 });
|
a := context.io.async(() -> (i64, !) { try context.io.sleep(10); rec(pl, 1, context.io.now_ms()); 111 });
|
||||||
b := context.io.async(() -> (bool, !) => { try context.io.sleep(20); rec(pl, 2, context.io.now_ms()); true });
|
b := context.io.async(() -> (bool, !) { try context.io.sleep(20); rec(pl, 2, context.io.now_ms()); true });
|
||||||
c := context.io.async(() -> (f64, !) => { try context.io.sleep(30); rec(pl, 3, context.io.now_ms()); 2.5 });
|
c := context.io.async(() -> (f64, !) { try context.io.sleep(30); rec(pl, 3, context.io.now_ms()); 2.5 });
|
||||||
|
|
||||||
// Race them. `a` (sleep 10) wins; `b` and `c` are cancelled — their
|
// Race them. `a` (sleep 10) wins; `b` and `c` are cancelled — their
|
||||||
// post-sleep work never runs (true cancellation).
|
// post-sleep work never runs (true cancellation).
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ main :: () -> i64 {
|
|||||||
ps := @s;
|
ps := @s;
|
||||||
print("outside: marker id = {}\n", mk.id);
|
print("outside: marker id = {}\n", mk.id);
|
||||||
push .{ data = xx @mk } {
|
push .{ data = xx @mk } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
m : *Marker = xx context.data; // inherited from the spawn-time context
|
m : *Marker = xx context.data; // inherited from the spawn-time context
|
||||||
print("inside fiber: context.data marker id = {}\n", m.id);
|
print("inside fiber: context.data marker id = {}\n", m.id);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ main :: () -> i64 {
|
|||||||
s := sched.Scheduler.init();
|
s := sched.Scheduler.init();
|
||||||
ps := @s; pl := @lg;
|
ps := @s; pl := @lg;
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
rec(pl, 1); // coordinator starts
|
rec(pl, 1); // coordinator starts
|
||||||
a := context.io.async(() -> (i64, !) => { rec(pl, 10); 100 }); // worker A — deferred
|
a := context.io.async(() -> (i64, !) { rec(pl, 10); 100 }); // worker A — deferred
|
||||||
b := context.io.async(() -> (i64, !) => { rec(pl, 20); 23 }); // worker B — deferred
|
b := context.io.async(() -> (i64, !) { rec(pl, 20); 23 }); // worker B — deferred
|
||||||
rec(pl, 2); // both spawned, neither has run
|
rec(pl, 2); // both spawned, neither has run
|
||||||
va := a.await() or { -1 }; // park; A runs, wakes us
|
va := a.await() or { -1 }; // park; A runs, wakes us
|
||||||
vb := b.await() or { -1 };
|
vb := b.await() or { -1 };
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ main :: () -> i64 {
|
|||||||
s := sched.Scheduler.init();
|
s := sched.Scheduler.init();
|
||||||
ps := @s; pl := @lg;
|
ps := @s; pl := @lg;
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
w := context.io.async(() -> (i64, !) => {
|
w := context.io.async(() -> (i64, !) {
|
||||||
rec(pl, 1); // worker started
|
rec(pl, 1); // worker started
|
||||||
try context.io.sleep(10); // park; cancel delivers Canceled HERE
|
try context.io.sleep(10); // park; cancel delivers Canceled HERE
|
||||||
rec(pl, 2); // POST-SUSPEND — must NEVER run
|
rec(pl, 2); // POST-SUSPEND — must NEVER run
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ main :: () -> i64 {
|
|||||||
s := sched.Scheduler.init();
|
s := sched.Scheduler.init();
|
||||||
ps := @s;
|
ps := @s;
|
||||||
push .{ io = xx s } {
|
push .{ io = xx s } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
a := context.io.async(() -> (i64, !) => { try context.io.sleep(5); raise error.Boom; });
|
a := context.io.async(() -> (i64, !) { try context.io.sleep(5); raise error.Boom; });
|
||||||
b := context.io.async(() -> (i64, !) => { try context.io.sleep(10); 42 });
|
b := context.io.async(() -> (i64, !) { try context.io.sleep(10); 42 });
|
||||||
winner := context.io.race(.(a = a, b = b));
|
winner := context.io.race(.(a = a, b = b));
|
||||||
if winner == {
|
if winner == {
|
||||||
case .a: (v) { print("winner: a = {}\n", v); }
|
case .a: (v) { print("winner: a = {}\n", v); }
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ main :: () -> i64 {
|
|||||||
ps := @s;
|
ps := @s;
|
||||||
pbase.* = gpa.alloc_count; // baseline: scheduler is live, no tasks yet
|
pbase.* = gpa.alloc_count; // baseline: scheduler is live, no tasks yet
|
||||||
push .{ io = xx s, allocator = xx gpa, data = null } {
|
push .{ io = xx s, allocator = xx gpa, data = null } {
|
||||||
ps.spawn(() => {
|
ps.spawn(() {
|
||||||
a := context.io.async(() -> (i64, !) => { try context.io.sleep(10); 100 });
|
a := context.io.async(() -> (i64, !) { try context.io.sleep(10); 100 });
|
||||||
b := context.io.async(() -> (i64, !) => { try context.io.sleep(20); 20 });
|
b := context.io.async(() -> (i64, !) { try context.io.sleep(20); 20 });
|
||||||
c := context.io.async(() -> (i64, !) => { try context.io.sleep(30); 3 });
|
c := context.io.async(() -> (i64, !) { try context.io.sleep(30); 3 });
|
||||||
psum.* = (a.await() or 0) + (b.await() or 0) + (c.await() or 0);
|
psum.* = (a.await() or 0) + (b.await() or 0) + (c.await() or 0);
|
||||||
});
|
});
|
||||||
ps.run();
|
ps.run();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#import "modules/ffi/objc_block.sx";
|
#import "modules/ffi/objc_block.sx";
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
cl := () => { print("noop block ran\n"); };
|
cl := () { print("noop block ran\n"); };
|
||||||
b : Block = xx cl;
|
b : Block = xx cl;
|
||||||
invoke_fn : (*Block) -> void abi(.c) = xx b.invoke;
|
invoke_fn : (*Block) -> void abi(.c) = xx b.invoke;
|
||||||
invoke_fn(@b);
|
invoke_fn(@b);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
x : i64 = 42;
|
x : i64 = 42;
|
||||||
y : i64 = 100;
|
y : i64 = 100;
|
||||||
cl := () => { print("x + y = {}\n", x + y); };
|
cl := () { print("x + y = {}\n", x + y); };
|
||||||
b : Block = xx cl;
|
b : Block = xx cl;
|
||||||
invoke_fn : (*Block) -> void abi(.c) = xx b.invoke;
|
invoke_fn : (*Block) -> void abi(.c) = xx b.invoke;
|
||||||
invoke_fn(@b);
|
invoke_fn(@b);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ g_sum: i32 = 0;
|
|||||||
g_tag: *void = null;
|
g_tag: *void = null;
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
cl := (n: i32, tag: *void) => {
|
cl := (n: i32, tag: *void) {
|
||||||
g_sum = n + 1;
|
g_sum = n + 1;
|
||||||
g_tag = tag;
|
g_tag = tag;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ invoke_once :: (b: *Block) {
|
|||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
x : i64 = 7;
|
x : i64 = 7;
|
||||||
invoke_once(xx () => {
|
invoke_once(xx () {
|
||||||
print("inline block, x = {}\n", x);
|
print("inline block, x = {}\n", x);
|
||||||
});
|
});
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ main :: () -> i32 {
|
|||||||
// (NSObject.new returns a +1 retained, NOT autoreleased), so this
|
// (NSObject.new returns a +1 retained, NOT autoreleased), so this
|
||||||
// is a smoke test of the helper's shape, not the runtime
|
// is a smoke test of the helper's shape, not the runtime
|
||||||
// behavior.
|
// behavior.
|
||||||
autoreleasepool(() => {
|
autoreleasepool(() {
|
||||||
inner := NSObject.new();
|
inner := NSObject.new();
|
||||||
if inner != null {
|
if inner != null {
|
||||||
inner.release();
|
inner.release();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ g_a: i64 = 0;
|
|||||||
g_b: i64 = 0;
|
g_b: i64 = 0;
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
cl := (a: i64, b: i64) => { g_a = a; g_b = b; };
|
cl := (a: i64, b: i64) { g_a = a; g_b = b; };
|
||||||
blk : Block = xx cl;
|
blk : Block = xx cl;
|
||||||
|
|
||||||
invoke_fn : (*Block, i64, i64) -> void abi(.c) = xx blk.invoke;
|
invoke_fn : (*Block, i64, i64) -> void abi(.c) = xx blk.invoke;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ main :: () {
|
|||||||
if n != null {
|
if n != null {
|
||||||
// `n` is narrowed HERE, but the closure body is a separate function:
|
// `n` is narrowed HERE, but the closure body is a separate function:
|
||||||
// `n` is `?i64` inside it, so this implicit unwrap must be rejected.
|
// `n` is `?i64` inside it, so this implicit unwrap must be rejected.
|
||||||
g := () => { takes_i64(n); };
|
g := () { takes_i64(n); };
|
||||||
g();
|
g();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
error: cannot use a value of type '?i64' where 'i64' is expected: an optional does not implicitly unwrap; force-unwrap with '!', supply a fallback with '?? <default>', bind it (`if v := ...`), or guard with '!= null'
|
error: cannot use a value of type '?i64' where 'i64' is expected: an optional does not implicitly unwrap; force-unwrap with '!', supply a fallback with '?? <default>', bind it (`if v := ...`), or guard with '!= null'
|
||||||
--> examples/optionals/0923-optionals-narrowing-no-closure-leak.sx:20:22
|
--> examples/optionals/0923-optionals-narrowing-no-closure-leak.sx:20:19
|
||||||
|
|
|
|
||||||
20 | g := () => { takes_i64(n); };
|
20 | g := () { takes_i64(n); };
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
g_s: string = "";
|
g_s: string = "";
|
||||||
|
|
||||||
main :: () -> i32 {
|
main :: () -> i32 {
|
||||||
cl := (s: string) => { g_s = s; };
|
cl := (s: string) { g_s = s; };
|
||||||
b : Block = xx cl;
|
b : Block = xx cl;
|
||||||
invoke_fn : (*Block, string) -> void abi(.c) = xx b.invoke;
|
invoke_fn : (*Block, string) -> void abi(.c) = xx b.invoke;
|
||||||
invoke_fn(@b, "hello");
|
invoke_fn(@b, "hello");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Multi-return CLOSURE types and lambda literals work via the same tuple
|
// Multi-return CLOSURE types and lambda literals work via the same tuple
|
||||||
// machinery as function multi-returns (D3): a `Closure() -> (A, B)` value's call
|
// machinery as function multi-returns (D3): a `Closure() -> (A, B)` value's call
|
||||||
// result destructures (`a, b := cb()`), single-binds with field access
|
// result destructures (`a, b := cb()`), single-binds with field access
|
||||||
// (`c := cb(); c.0`), and a `() => { return v1, v2; }` lambda literal satisfies a
|
// (`c := cb(); c.0`), and a `() { return v1, v2; }` lambda literal satisfies a
|
||||||
// multi-return closure parameter. No dedicated ClosureInfo marker is needed —
|
// multi-return closure parameter. No dedicated ClosureInfo marker is needed —
|
||||||
// the return slots ride as the reused `.tuple` TypeId, consistent with the
|
// the return slots ride as the reused `.tuple` TypeId, consistent with the
|
||||||
// function-decl multi-return surface.
|
// function-decl multi-return surface.
|
||||||
@@ -13,14 +13,14 @@ apply :: (cb: Closure() -> (i32, bool)) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main :: () -> i64 {
|
main :: () -> i64 {
|
||||||
cb : Closure() -> (i32, bool) = () => { return 7, true; };
|
cb : Closure() -> (i32, bool) = () { return 7, true; };
|
||||||
x, y := cb();
|
x, y := cb();
|
||||||
print("{} {}\n", x, y); // 7 true
|
print("{} {}\n", x, y); // 7 true
|
||||||
|
|
||||||
c := cb(); // single-bind + positional field access
|
c := cb(); // single-bind + positional field access
|
||||||
print("{} {}\n", c.0, c.1); // 7 true
|
print("{} {}\n", c.0, c.1); // 7 true
|
||||||
|
|
||||||
r := apply(() => { return 9, true; }); // lambda literal as the closure arg
|
r := apply(() { return 9, true; }); // lambda literal as the closure arg
|
||||||
print("{}\n", r); // 9
|
print("{}\n", r); // 9
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ main :: () {
|
|||||||
w := (a: i64, b: i64) -> i64 => a + b;
|
w := (a: i64, b: i64) -> i64 => a + b;
|
||||||
t := .{40, 2};
|
t := .{40, 2};
|
||||||
out : i64 = 0; po := @out;
|
out : i64 = 0; po := @out;
|
||||||
captured :: () => { po.* = w(..t); }; // tuple spread inside a closure → panics
|
captured :: () { po.* = w(..t); }; // tuple spread inside a closure → panics
|
||||||
captured();
|
captured();
|
||||||
print("out: {}\n", out); // want: out: 42 (or a clean diagnostic)
|
print("out: {}\n", out); // want: out: 42 (or a clean diagnostic)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ impl Into(*NSString) for string {
|
|||||||
// ─── Autoreleasepool (M4.A) ──────────────────────────────────────────────
|
// ─── Autoreleasepool (M4.A) ──────────────────────────────────────────────
|
||||||
// Foundation factory methods (`NSString.stringWithUTF8String:`,
|
// Foundation factory methods (`NSString.stringWithUTF8String:`,
|
||||||
// `[NSArray array]`, ...) return autoreleased objects — valid until the
|
// `[NSArray array]`, ...) return autoreleased objects — valid until the
|
||||||
// current pool drains. Wrap such code in `autoreleasepool(() => { ... })`
|
// current pool drains. Wrap such code in `autoreleasepool(() { ... })`
|
||||||
// so the pool drains deterministically at block end.
|
// so the pool drains deterministically at block end.
|
||||||
//
|
//
|
||||||
// Stdlib helper, not a language keyword. The closure call adds a frame —
|
// Stdlib helper, not a language keyword. The closure call adds a frame —
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ sx_run_boxed_closure :: (arg: *void) {
|
|||||||
// the dealloc is a no-op.
|
// the dealloc is a no-op.
|
||||||
run_env := b.run.env;
|
run_env := b.run.env;
|
||||||
worker_env := b.worker_env;
|
worker_env := b.worker_env;
|
||||||
if run_env != null { context.allocator.dealloc_bytes(run_env); }
|
if run_env != null { context.allocator.dealloc_bytes(run_env); }
|
||||||
if worker_env != null { context.allocator.dealloc_bytes(worker_env); }
|
if worker_env != null { context.allocator.dealloc_bytes(worker_env); }
|
||||||
context.allocator.dealloc_bytes(xx b);
|
context.allocator.dealloc_bytes(xx b);
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ async :: ufcs (io: Io, worker: Closure() -> ($R, !)) -> *Future($R) {
|
|||||||
// captured by-value into `run`'s env below, otherwise unreachable). `null` for
|
// captured by-value into `run`'s env below, otherwise unreachable). `null` for
|
||||||
// a capture-free worker.
|
// a capture-free worker.
|
||||||
b.worker_env = worker.env;
|
b.worker_env = worker.env;
|
||||||
b.run = () => {
|
b.run = () {
|
||||||
f.value = worker() catch {
|
f.value = worker() catch {
|
||||||
if f.canceled.load(.acquire) { f.state = .canceled; }
|
if f.canceled.load(.acquire) { f.state = .canceled; }
|
||||||
else { f.state = .failed; }
|
else { f.state = .failed; }
|
||||||
@@ -282,7 +282,7 @@ await :: ufcs (f: *Future($R)) -> ($R, !IoErr) {
|
|||||||
f.consumed = true;
|
f.consumed = true;
|
||||||
fut_release(f); // frees the Future iff the worker has also finished
|
fut_release(f); // frees the Future iff the worker has also finished
|
||||||
if canceled { raise error.Canceled; }
|
if canceled { raise error.Canceled; }
|
||||||
if failed { raise error.Failed; }
|
if failed { raise error.Failed; }
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ impl Io for Scheduler {
|
|||||||
print("sched: spawn_raw called with a null entry function\n");
|
print("sched: spawn_raw called with a null entry function\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
f := self.spawn(() => {
|
f := self.spawn(() {
|
||||||
entry_fn : (*void) -> void = xx entry;
|
entry_fn : (*void) -> void = xx entry;
|
||||||
entry_fn(arg);
|
entry_fn(arg);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user