|
| 1 | +# Automatic host binding selection design |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +Make generated default host bindings select the most capable safe VM binding from the annotated Rust function signature. In particular, a function such as `fn foo(x: i64) -> i64` must bind through `bind_static_non_yielding_args_function` so native JIT traces may continue across the call. |
| 6 | + |
| 7 | +## Scope |
| 8 | + |
| 9 | +This change covers `#[pd_host_function]` implementations discovered by the RustScript build generator and the equivalent `HostFunctionRegistry` cached-binding path. Dynamic trait-object bindings remain explicit public APIs because a plain annotated function cannot supply a factory or stateful trait object. |
| 10 | + |
| 11 | +## Signature classification |
| 12 | + |
| 13 | +Introduce one shared build-time classification with these ordered rules: |
| 14 | + |
| 15 | +1. A function with a `Vm` context parameter uses `StaticStack`. |
| 16 | +2. An args-only function whose normalized return type is `CallOutcome`, including `VmResult<CallOutcome>` and `HostResult<CallOutcome>`, uses `StaticArgs`. |
| 17 | +3. Every other valid args-only annotated function uses `StaticNonYieldingArgs`. |
| 18 | + |
| 19 | +The third rule is safe because the generated wrapper converts all supported ordinary outputs through `IntoVmValue` into exactly one `Value`. This includes implicit `()`, explicit `()`, and `Option<T>`, which become `Value::Null` when appropriate. `VmResult<T>` and `HostResult<T>` may still return an error; successful calls return exactly one value synchronously. |
| 20 | + |
| 21 | +`CallOutcome` stays on the general static args ABI because it can represent no return value, halt, yield, or pending work. Any signature that cannot be classified safely falls back to the general compatible static binding. |
| 22 | + |
| 23 | +## Generated binding paths |
| 24 | + |
| 25 | +The build generator will use the same classification for both generated surfaces: |
| 26 | + |
| 27 | +| Kind | Registry generation | Direct VM generation | |
| 28 | +|---|---|---| |
| 29 | +| `StaticStack` | `register_static_stack` | `bind_static_stack_function` | |
| 30 | +| `StaticArgs` | `register_static_args` | `bind_static_args_function` | |
| 31 | +| `StaticNonYieldingArgs` | `register_static_non_yielding_args` | `bind_static_non_yielding_args_function` | |
| 32 | + |
| 33 | +This keeps `HostFunctionRegistry::bind_vm_cached` and direct default-host binding behavior equivalent. |
| 34 | + |
| 35 | +## Registry support |
| 36 | + |
| 37 | +Extend `HostFunctionRegistry` with a static non-yielding args entry and public registration method. Binding plans will preserve that entry kind and install it through `Vm::register_static_non_yielding_args_function`. |
| 38 | + |
| 39 | +Re-registration under an existing name replaces the entry kind and invalidates the plan cache, matching the current registry behavior for other host ABI kinds. |
| 40 | + |
| 41 | +## Tests |
| 42 | + |
| 43 | +Add focused tests for: |
| 44 | + |
| 45 | +- signature classification for plain values, implicit and explicit unit, options, fallible values, `CallOutcome`, wrapped `CallOutcome`, and VM-aware functions; |
| 46 | +- generated registry and direct-bind code selecting the expected method; |
| 47 | +- registry cached binding preserving the non-yielding host kind; |
| 48 | +- execution of a representative typed host function; |
| 49 | +- native JIT tracing through a generated or registry-bound non-yielding args host call when the JIT feature is enabled. |
| 50 | + |
| 51 | +Run the narrow tests first, then the relevant workspace tests, formatting, Clippy, and build checks without touching unrelated worktree changes. |
0 commit comments