diff --git a/docs/design-abi-format.md b/docs/design-abi-format.md new file mode 100644 index 0000000..8abba65 --- /dev/null +++ b/docs/design-abi-format.md @@ -0,0 +1,75 @@ +# Design: ABI format selection (`raw` vs `cbor`) + +Status: **direction decided, open for refinement.** How a library author tells the +compiler which wire format a boundary-crossing proc uses. Applies to the whole +pragma family (`{.ffi.}`, `{.ffiHost.}`, `{.ffiCtor.}`, `{.ffiDtor.}`). + +## The two formats + +- **`raw`** — native, zero-serialization C-POD ABI. Same-process; the request/ + result is a C struct passed/cast directly under the deep-copy + + callback-lifetime ownership rule. The common path. +- **`cbor`** — CBOR-encoded buffer. For IPC / generic / cross-language callers + where serialization is required anyway. + +## Decision + +**A1 — per-proc pragma argument** (chosen). The format is an argument on the +pragma, read by the macro at compile time: + +```nim +proc echo(req: EchoReq): Future[Result[EchoResp, string]] {.ffi: raw.} +proc echo(req: EchoReq): Future[Result[EchoResp, string]] {.ffi: cbor.} + +proc fetchProfile(id: string): Future[Result[Profile, string]] {.ffiHost: cbor.} +``` + +Matches the existing `{.ffiEvent: "wire_name".}` precedent (pragmas already take +args in this codebase). Local, granular, lets one library mix both formats. + +**C2 — library-wide default** (chosen, layered on top). Set the default once at +`declareLibrary`; a per-proc pragma arg overrides it: + +```nim +declareLibrary("my_app", MyApp, defaultAbiFormat = raw) +# every {.ffi.}/{.ffiHost.}/… is `raw` unless it says otherwise +``` + +So the common case stays terse, with per-proc control when needed. + +**Rejected:** +- **Global compile flag** (`-d:ffiFormat=…`) — discarded. All-or-nothing, + action-at-a-distance, can't mix formats in one library. `defaultAbiFormat` + replaces its only virtue (a single default) without the downsides. +- Distinct pragma names (`{.ffiCbor.}`, `{.ffiHostCbor.}`) — combinatorial + explosion across the pragma family. +- Format on the data type (`type T {.ffi: cbor.}`) — misattributes a transport + property to the data; breaks when one type is used by both a raw and a cbor + proc. + +## Sketch + +```nim +type AbiFormat* = enum + raw # native zero-serialization C-POD (same-process) + cbor # CBOR buffer (IPC / generic) + +# two overloads, like ffiEvent: no-arg form defaults to the library default +macro ffi*(prc: untyped): untyped = ... # uses defaultAbiFormat +macro ffi*(fmt: static[AbiFormat], prc: untyped): untyped = ... # explicit override +``` + +## Open questions (for further discussion) + +- **Enum name / spelling.** `AbiFormat` with values `raw` / `cbor`? (`raw` over + `native` per current preference.) +- **Resolution order.** proc pragma arg → `defaultAbiFormat` → built-in fallback + (`raw`?). Confirm the fallback when `declareLibrary` sets no default. +- **Symbol emission.** Today `{.ffi.}` emits BOTH `` and `_cbor`. Does + a per-proc format mean we emit only the chosen symbol, or keep emitting both + and let the format arg pick the *primary*? (Leaning: emit only the chosen one; + `_cbor` suffix stays only when both are explicitly wanted.) +- **Codegen impact.** Each generator (c/go/cpp/rust/swift/kotlin) must honour the + per-proc format when emitting wrappers + registration symbols. +- **Future A3.** Whether to later expose an orthogonal `{.ffi, wire: cbor.}` + transport pragma that composes across the family (deferred; A1+C2 first). diff --git a/docs/future-work.md b/docs/future-work.md index 767cdc9..5ac0a91 100644 --- a/docs/future-work.md +++ b/docs/future-work.md @@ -46,6 +46,13 @@ Export `_abi_descriptor()` returning the schema (CBOR/JSON) so a host can validate compatibility at load time. Addresses the deferred CBOR wire-versioning concern. +## Cross-cutting decisions + +- **ABI format selection** ([design-abi-format.md](design-abi-format.md)) — per-proc + pragma arg `{.ffi: raw.}` / `{.ffi: cbor.}` (default native/`raw`), with a + library-wide `declareLibrary(defaultAbiFormat = …)` override. Global compile + flag discarded. Applies to `{.ffiHost.}` too. Direction decided, design only. + ## Adjacent / parallel tracks (already discussed elsewhere) - **seq/Option + multi-struct param marshaling parity** for the Swift (#59) and