You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Deno source-to-source host→guest callback ABI (blocks withProgress/registerCommand/onDidSave) #199
The Deno/Node source-to-source backend (lib/codegen_deno.ml) has no host→guest callback mechanism. It explicitly has "no handle table" (codegen_deno.ml:9) and ExprHandle lowering ignores handlers. The only working callback path in the estate is the wasm adapter packages/affine-vscode/mod.js, which resolves a handler Int via inst.exports.__indirect_function_table (wrapHandler).
Consequently, on the Deno source-to-source target, any extern that takes an AffineScript thunk the host must invoke is unimplementable:
The callback ABI is a distinct, larger, one-way-door design (it dictates how every host→guest callback is shaped on the source-to-source backend, not just async), so it is tracked here rather than expanding #103.
Design space (not yet decided)
Guest exports a thunk-dispatch entrypoint; codegen_deno emits a JS-side handle registry the adapter calls back into.
Alternatively: closures-as-values lowering so handlers are real JS functions passed directly (no Int handle indirection on the source-to-source path at all).
Acceptance
registerCommand, onDidSaveTextDocument, withProgress all functional on --deno-esm / --vscode-extension output.
rsr-certifier vscode pilot fully restored (the withProgress-wrapped checks).
Root cause: AffineScript has a deliberate convention (lib/typecheck.ml:752, mirrored in ExprLambda synth at ~742) that a zero-parameter function is typed as its bare return type, not Unit -> T. But the type syntaxfn() -> T lowers to TArrow(Unit, _, T, _). So zero-arg lambda values and the fn() -> Ttype don't unify. Changing that convention is a language-wide one-way door (it governs every zero-arg call site) and is out of scope here.
Resolution (no compiler change)
Handler bindings use an explicit Unit param: handler: fn(Unit) -> R; consumers write fn(_u: Unit) => { ... }. Slightly more ceremony than fn(), but it is the honest, zero-risk realisation of the function-value ABI — the zero-arg-fn convention stays intact.
editors/vscode/src/extension.affine: real 5-command extension using a numbered-handler-dispatch idiom (handlers 0–4) — behaviour-preserving rewrite to passed function values.
Tracked as PR-5a (binding + example + editors extension, 253-gated), PR-5b Deno lowering, PR-5c wasm marshalling, PR-5d rsr-certifier pilot.
Closing — host->guest callback ABI complete (STAGE-B).
The function-value callback ABI is shipped on main:
feat(vscode): function-value callback ABI — registerCommand/onDidSave/withProgress (Refs #199) #201 — fn-typed handler bindings + typecheck. ABI is fn(Unit)->R (consumer writes fn(u: Unit) => ...). Root cause documented: AffineScript types a 0-param fn as its bare return type (typecheck.ml:752), so a literal fn()->T would collapse; fn(Unit)->R avoids it with no compiler change. registerCommand / onDidSaveTextDocument / withProgressNotification handlers migrated.
PR-5b — Deno source-to-source backend needed zero codegen change (lambdas already lower to JS arrows); proven, no PR required.
This unblocked withProgress / registerCommand / onDidSave. The rsr-certifier pilot that would have demonstrated it in a real consumer (was PR-5d-B) is decoupled to an independent standards-repo follow-up and no longer gates this issue.
Closed on owner approval (joint close, 2026-05-18).
Activity
feat(affine-vscode): languageClientSendRequest host binding (Refs #103)…
hyperpolymath commentedon May 18, 2026
Decisive finding — ABI shape resolved without a compiler change
Investigation of the chosen function-value ABI: a function-typed extern param typechecks today — provided the handler takes an explicit argument:
extern fn onSave(handler: fn(Int) -> Int)+onSave(fn(x: Int) => x)→ ✅ typechecksextern fn onCmd(handler: fn(Unit) -> Int)+onCmd(fn(u: Unit) => 0)→ ✅ typechecksextern fn onCmd(handler: fn() -> Int)+onCmd(fn() => 0)→ ❌TypeMismatch((Unit -> Int), Int)Root cause: AffineScript has a deliberate convention (
lib/typecheck.ml:752, mirrored inExprLambdasynth at ~742) that a zero-parameter function is typed as its bare return type, notUnit -> T. But the type syntaxfn() -> Tlowers toTArrow(Unit, _, T, _). So zero-arg lambda values and thefn() -> Ttype don't unify. Changing that convention is a language-wide one-way door (it governs every zero-arg call site) and is out of scope here.Resolution (no compiler change)
Handler bindings use an explicit
Unitparam:handler: fn(Unit) -> R; consumers writefn(_u: Unit) => { ... }. Slightly more ceremony thanfn(), but it is the honest, zero-risk realisation of the function-value ABI — the zero-arg-fn convention stays intact.Scope (consumers to migrate at root)
stdlib/Vscode.affine:registerCommand,onDidSaveTextDocument(+withProgressNotificationwork param from feat(stdlib): async-extern binding surface — Thenable + withProgress/sendRequest (Refs #103) #198) →fn(Unit) -> R.examples/vscode_extension_minimal.affine:registerCommand("…", 0)→ lambda.editors/vscode/src/extension.affine: real 5-command extension using a numbered-handler-dispatch idiom (handlers 0–4) — behaviour-preserving rewrite to passed function values.Tracked as PR-5a (binding + example + editors extension, 253-gated), PR-5b Deno lowering, PR-5c wasm marshalling, PR-5d rsr-certifier pilot.
Refs #199
feat(vscode): function-value callback ABI — registerCommand/onDidSave…
fix(affine-vscode): marshal closure-pointer handlers, not table indic…
feat(thenable): wasm-path thenable-resolution primitives (Closes #205) (
hyperpolymath commentedon May 18, 2026
Closing — host->guest callback ABI complete (STAGE-B).
The function-value callback ABI is shipped on
main:This unblocked withProgress / registerCommand / onDidSave. The rsr-certifier pilot that would have demonstrated it in a real consumer (was PR-5d-B) is decoupled to an independent standards-repo follow-up and no longer gates this issue.
Closed on owner approval (joint close, 2026-05-18).
feat(stdlib): add httpPostJson async-extern for out-of-process JSON P…
feat(stdlib): add jsonField extern for minimal JSON field extraction (#…
feat(rsr-certifier): BoJ-first 3-tier compliance in the VS Code exten…
feat(affine-vscode): implement withProgressNotification runtime adapt…
feat: #225 typed-wasm Http — ADR-013 + PR1 verified Thenable foundati…
feat(codegen): #225 PR2 — WasmGC CPS transform, Async base case (ADR-013
feat(codegen): #225 PR3a — WasmGC CPS transform, multi-var live-local…
feat(http): #225 PR3d — typed Response reader (ADR-013 §value-reconst…
docs(http): #225 PR4 — joint-close #160 + #225 (ADR-013 delivery comp…
docs(http): #225 PR4 — joint-close #160 + #225 (ADR-013 delivery comp…
feat(codegen): #234 S3 — effect-threaded async-boundary detection (AD…
test(codegen): #235 — engine-validate the #199 closure ABI independen…
feat(wasi): #180 ADR-015 S4a — clock_now_ms builtin (preview1 → wasi:…
feat(wasi): #180 ADR-015 S4b — env_count/arg_count + multi-import ind…