fibers B1.2: record review findings — async surface blocked on 0151 (widened)
Adversarial review of 45d869d: the Io infrastructure (both materializers,
push-inherit, 37 .ir regens, !-lint) is correct + landed; but await/cancel
(*Future($R)) are uncallable in EVERY form because sx can't infer a generic
$T from a pointer-wrapped arg. Widened issue 0151 to that root (repro:
unbox(b: *Box($T)) -> $T). Checkpoint: B1.2 partially landed; next = fix 0151
generic inference -> make await/cancel callable -> add 1805/1806 -> B1.3.
This commit is contained in:
@@ -4,8 +4,31 @@ Companion to [PLAN-FIBERS.md](PLAN-FIBERS.md). Update after every step (one step
|
||||
per the cadence rule). New corpus category: `18xx` concurrency.
|
||||
|
||||
## Last completed step
|
||||
**B1.1 (per-fiber `context` root) — DONE. Zero compiler change (confirmed by probe).** The
|
||||
fiber-spawn context convention works end-to-end with ordinary language features:
|
||||
**B1.2 (Io capability) — PARTIALLY LANDED + adversarially reviewed. Infrastructure GOOD;
|
||||
async SURFACE blocked on a generic-inference compiler bug (issue 0151, widened).**
|
||||
Commits `a1b14f0` (lock) + `45d869d` (Io capability) + `3eeb965` (issue 0151). Suite green
|
||||
**726/0**, master clean.
|
||||
- **LANDED + review-confirmed correct** (commit 45d869d): `Io :: protocol #inline`
|
||||
(spawn_raw/suspend_raw/ready/poll/now_ms/arm_timer) + `io` field on `Context`
|
||||
(`{allocator; data; io}`, io LAST); BOTH `__sx_default_context` materializers
|
||||
(protocol.zig + comptime_vm.zig) build an identical CBlockingIo→Io vtable (review verified
|
||||
byte-for-byte agreement; `context.io.now_ms()` dispatches at runtime AND comptime); the
|
||||
`push Context.{…}` omitted-field-**inherits-ambient** fix (review: correct, right fix, no
|
||||
bad blast radius); `library/modules/std/io.sx` (`Future($R)`, `CBlockingIo`,
|
||||
`async`/`await`/`cancel`); the `!`-protocol-impl-lint suppression; 37 `.ir` regens
|
||||
(review: pure layout/type-table, no error text, zero .exit/.stdout/.stderr change).
|
||||
- **BLOCKED — async surface non-functional:** `await`/`cancel` take `*Future($R)` and are
|
||||
**uncallable in EVERY form** (not just UFCS) — sx can't infer a generic `$T` from a
|
||||
pointer-wrapped arg (`*Future($R)`). `async(...)` (create) works via explicit call and
|
||||
produces a correct `.ready` Future, but you can't `await` it. Root bug = **issue 0151
|
||||
(WIDENED)**: infer `$T` from `*T`-wrapped params + closure-return-via-pack + UFCS dispatch.
|
||||
Minimal repro: `unbox :: (b: *Box($T)) -> $T` fails to infer `T`.
|
||||
- **No async example in the corpus** (1805 was removed because it needs the blocked surface)
|
||||
→ the green suite does NOT cover async. Restore `1805` (async/await) + add `1806` (cancel)
|
||||
once 0151 is fixed.
|
||||
|
||||
### Earlier — B1.1 (per-fiber `context` root) — DONE. Zero compiler change (confirmed by probe).
|
||||
The fiber-spawn context convention works end-to-end with ordinary language features:
|
||||
- `snap := context` captures the spawner's `Context` as a value;
|
||||
- the snapshot is stored in a struct (the stand-in `Fiber`);
|
||||
- a trampoline running under a *different* ambient context installs the fiber's stored root
|
||||
@@ -140,37 +163,26 @@ fibers/Io/scheduler code yet. Grounded floor facts:
|
||||
boundary; a sharper sx diagnostic for it is a candidate polish, not a blocker.
|
||||
|
||||
## Next step
|
||||
**DECISION (user, 2026-06-20): B1.2 async = LAMBDA workers only for now; named-fn workers
|
||||
(`async(read_a, conn)`) are DEFERRED.** Named-fn support needs a new `::` callable-parameter
|
||||
language feature (`fn :: (..$args) -> $R` param) that doesn't exist yet — it failed 3 worker
|
||||
attempts; the partial WIP (parser+scaffolding done, type-pack/return inference INCOMPLETE —
|
||||
panics on every form) is saved at `.sx-tmp/wip-callable-params/patch.diff` for a dedicated
|
||||
future effort. So B1.2 ships with the lambda idiom below; call sites use
|
||||
`context.io.async((c) => read_a(c), conn)` until the `::` feature lands.
|
||||
**Fix issue 0151 (generic inference through a pointer) so `await`/`cancel` become callable —
|
||||
then complete B1.2's async surface.** Sequence:
|
||||
1. **Fix 0151 (WIDENED):** make the generic-inference engine bind `$T` from a pointer-wrapped
|
||||
param (`b: *Box($T)` ⇒ `unbox(@b)` infers `T`), which unblocks `await`/`cancel`
|
||||
(`*Future($R)`). The same bug has two more faces — `$R` from a closure-return via a pack,
|
||||
and via UFCS dot-dispatch (the original 0151 + the `f.await()` SIGTRAP). Acceptance cases
|
||||
are in `issues/0151-...md`. This is generic-inference-engine work
|
||||
(`src/ir/lower/generic.zig` `extractTypeParam` + the UFCS call path) — its own focused step.
|
||||
2. **Restore/add the async examples:** `examples/1805-concurrency-io-blocking-async.sx`
|
||||
(`context.io.async((a:i64,b:i64)->i64 => a+b, 40, 2).await()` → 42) + `1806-...-io-cancel.sx`
|
||||
(cancel → state `.canceled`). lock→green. Regen `.ir` only after green; confirm layout-only.
|
||||
3. Then B1.2 is truly done → proceed to **B1.3 (fiber runtime)**.
|
||||
|
||||
**B1.2 — resume now (UNBLOCKED, no compiler fix needed).** Re-land from the saved WIP
|
||||
(`.sx-tmp/b12-wip/`): keep the verified-working parts — the `Io` protocol on `Context`, both
|
||||
`__sx_default_context` materializers (protocol.zig + comptime_vm.zig), the push-inherit-omitted
|
||||
fix (stmt.zig `lowerPush` — omitted `push Context.{...}` fields inherit the ambient ctx; the
|
||||
correct fix, NOT per-site `io = context.io` edits across the 17 sites), and the
|
||||
`!`-impl-warning fix. **Rewrite the async/await layer to the CORRECT idiom** (verified live):
|
||||
**Deferred (do NOT block B1.2 on these):** issue **0150** (`void` struct field SIGTRAP) — only
|
||||
`Future(void)`/`timeout`, which are B1.4. The **`::` callable-parameter feature** (named-fn
|
||||
async workers `async(read_a, conn)`) — WIP at `.sx-tmp/wip-callable-params/patch.diff` (parser
|
||||
done, inference incomplete); a dedicated effort; lambda workers are the B1.2 idiom meanwhile.
|
||||
|
||||
async :: (io: Io, worker: Closure(..$args) -> $R, ..$args) -> Future($R) {
|
||||
f : Future($R) = ---; // `= ---` + field-assign, NOT a struct-literal return
|
||||
f.value = worker(..args); // blocking impl: run to completion
|
||||
f.state = .ready;
|
||||
return f;
|
||||
}
|
||||
|
||||
Worker is a **lambda** with **annotated params** (`(a: i64, b: i64) -> i64 => …`); name it
|
||||
`async` (NOT `run` — `run` collides with `process.run` re-exported by std.sx and is silently
|
||||
shadowed). `Future($R)` is a parameterized `struct($T)` (so the bare-`-> $R`-return inference
|
||||
gap is auto-avoided). **Avoid `Future(void)`** (issue 0150 SIGTRAP) — B1.2 supports non-void
|
||||
workers; `timeout`/`Future(void)` defer to B1.4. Add `examples/1805-concurrency-io-blocking-
|
||||
async.sx` (lock→green) + `1806-concurrency-io-cancel.sx`. Regen `.ir` ONLY after green
|
||||
(`-Dupdate-goldens`) — adding `Io` to the prelude shifts many `.ir` type tables; confirm the
|
||||
diff is ONLY layout/numbering + the new vtable, NO error text. `Context` layout settled:
|
||||
`{ allocator; data; io; }` (allocator index 0 fixed by `call.zig:1229`, `io` last).
|
||||
`Context` layout settled: `{ allocator; data; io; }` (allocator index 0 fixed by
|
||||
`call.zig:1229`, io last). Io protocol + materializers + push-inherit are LANDED + reviewed.
|
||||
|
||||
## Known issues / capability gaps
|
||||
- **🔴 B1.2 BLOCKERS (both filed, both standalone-reproducible, both independent of the Io
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
# 0151 — UFCS dot-call: `$R` inferred from a closure's RETURN type via a variadic pack is left unresolved (LLVM SIGTRAP)
|
||||
# 0151 — generic type-var not inferred through a pointer / via UFCS (LLVM SIGTRAP / "cannot infer")
|
||||
|
||||
## WIDENED (adversarial review of B1.2, 2026-06-21)
|
||||
The UFCS-closure-return-pack case below is one symptom of a BROADER generic-inference
|
||||
gap: **sx cannot infer a generic `$T` from a POINTER-wrapped argument.** Minimal repro,
|
||||
no UFCS / no pack / no closure involved:
|
||||
```sx
|
||||
Box :: struct ($T: Type) { v: T; }
|
||||
unbox :: (b: *Box($T)) -> $T { return b.v; }
|
||||
// unbox(@b) → error: cannot infer generic type parameter 'T' for 'unbox'
|
||||
```
|
||||
This blocks `await`/`cancel` in `library/modules/std/io.sx` (both take `*Future($R)`) —
|
||||
they are **uncallable in EVERY form** (explicit `await(@f)` → "cannot infer 'R'"; UFCS
|
||||
`f.await()` → SIGTRAP). So B1.2's async layer can CREATE a Future (`async(...)` works) but
|
||||
cannot AWAIT it. Fix scope is the generic-inference engine (infer `$T` from `*T`-wrapped
|
||||
params, and from closure-return-via-pack, and through UFCS dot-dispatch) — not the Io lib.
|
||||
The two symptoms below + the `*Box($T)` repro above are the acceptance cases.
|
||||
|
||||
## Symptom
|
||||
|
||||
|
||||
Reference in New Issue
Block a user