Repository navigation
Preserve legacy HostAwait and add opt-in fixed-arity v2 APIs - #862
Maksym (maksym-mishchenko) wants to merge 4 commits into
Conversation
BREAKING CHANGE: registered HostAwait handlers receive ordered argument arrays; FFI and C# registrations require declared input counts, and Compiler::alloc_register returns Result. Deploy matching native/managed bindings and handlers and recompile policies when adopting packed registered calls. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve the input/data register caches across conditionally executed output binding plans so an undefined host argument cannot cause a later negated root access to allow incorrectly. Cover both caches and execution modes, defined output controls and VM reuse. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
BREAKING CHANGE: Array-packed fixed-arity registrations require explicit v2 APIs; legacy registrations use one raw Value. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The arity-based preflight incorrectly counts reused registers as new allocations, rejecting the second of two 253-input lookup calls even though actual register use remains within the limit.
1 open issue
What changed in this PR
This PR preserves Regorus’s legacy raw-value HostAwait contract while adding opt-in v2 APIs for fixed-arity, ordered-array payloads across Rust, native, and C# compilation.
Changes:
- Restore legacy one-input registrations and add separate v2 registration and compilation paths.
- Keep Undefined-input skipping specific to v2 calls, with explicit registration and descriptor validation.
- Expand compatibility regressions and document migration and checked register-allocation errors.
| File | Description |
|---|---|
| tests/rvm/rego/mod.rs | Separate legacy and v2 test registrations. |
| tests/rvm/rego/cases/registered_host_await.yaml | Cover legacy raw values and Undefined behavior. |
| tests/rvm/compiler.rs | Update v2 tests and add legacy compatibility checks. |
| src/languages/rego/compiler/rules.rs | Add compilation with separate registration lists. |
| src/languages/rego/compiler/mod.rs | Separate legacy and v2 registration contracts. |
| src/languages/rego/compiler/function_calls.rs | Distinguish raw and packed call compilation. |
| src/languages/rego/compiler/error.rs | Remove the dedicated register-overflow variant. |
| src/languages/rego/compiler/destructuring.rs | Adapt allocator errors for anyhow compatibility. |
| src/languages/rego/compiler/core.rs | Report register overflow through general compiler errors. |
| docs/rvm/vm-runtime.md | Clarify v2-only response-queue skipping. |
| docs/rvm/instruction-set.md | Document both contracts and allocator compatibility. |
| CHANGELOG.md | Record restored compatibility and the Rust API break. |
| bindings/ffi/src/rvm.rs | Add v2 descriptors, export, conversion, and validation tests. |
| bindings/ffi/CHANGELOG.md | Describe legacy layout restoration and v2 additions. |
| bindings/csharp/Regorus/Program.cs | Expose explicit v2 compilation. |
| bindings/csharp/Regorus/NativeMethods.cs | Declare separate native descriptors and v2 import. |
| bindings/csharp/Regorus/ModuleMarshalling.cs | Marshal legacy and v2 descriptors separately. |
| bindings/csharp/Regorus/Compiler.cs | Restore the legacy constructor and add the v2 type. |
| bindings/csharp/Regorus.Tests/RvmProgramTests.cs | Test legacy, v2, and mixed registrations. |
| bindings/csharp/README.md | Update examples and explain contract selection. |
| bindings/csharp/API.md | Document v2 payloads and native-export requirements. |
| bindings/cpp/rvm_tests.cpp | Test v2 strides and legacy raw arrays. |
| bindings/c/rvm_tests.c | Test v2 strides and legacy raw arrays. |
Review effort: Balanced
| } | ||
|
|
||
| self.ensure_register_capacity(3)?; | ||
| self.ensure_register_capacity(*arg_count)?; |
| let b = unsafe { &*(base.add(offset) as *const RegorusHostAwaitBuiltin) }; | ||
| let name = from_c_str(b.name) | ||
| .map_err(|e| anyhow!("invalid host-await builtin name at index {i}: {e}"))?; | ||
| result.push((name, 1)); |
There was a problem hiding this comment.
High: Do not silently reinterpret released v0.14.0 descriptors as raw legacy registrations.
v0.14.0 was published on October 27 and shipped RegorusHostAwaitBuiltin { name, arg_count } with packed-array semantics under this same export. Its 16-byte descriptor is accepted by the larger-stride validation, but this line discards its arity. For example, an existing C caller registering {"pair", 2} and compiling allow := pair(1, 2) now registers a one-input call: 2 becomes an output constraint, and the host receives raw 1 instead of [1, 2]. With a host response of true, the rule becomes undefined instead of returning true. One-input callers also change payload shape and Undefined response-queue consumption.
Please preserve the shipped descriptor semantics, or explicitly version/reject incompatible descriptors rather than silently accepting them. The managed migration also removes the released HostAwaitBuiltin(string, int) constructor and ArgCount, so additive compatibility requires preserving those members or clearly documenting the breaking migration. Add a released-client fixture rather than migrating every packed caller to v2.
| #[error("Unknown function: '{name}'")] | ||
| UnknownFunction { name: String }, | ||
|
|
||
| #[error( |
There was a problem hiding this comment.
Low: Retain the released public RegisterOverflow variant.
CompilerError is publicly re-exported, and v0.14.0 shipped RegisterOverflow { current, additional }. Removing it breaks downstream code that names the variant in a match and replaces a structured diagnostic with General. This is a separate API removal from the allocator-signature change described in the changelog.
Please retain the variant and return it from the checked allocator. alloc_register_compat already stringifies errors at the anyhow/no_std boundary, so that conversion does not require deleting the public variant. Keep a regression assertion referencing the structured variant instead of replacing it with a General assertion.

Summary
I restored the legacy raw one-value HostAwait interfaces and added separate, opt-in v2 registration APIs for fixed arities.
HostAwaitBuiltin(string)surface.Compatibility
Legacy callers keep their original native symbols and raw payloads. V2 calls require the new native export, including when the v2 registration list is empty; there is no silent downgrade. Duplicate registrations, descriptor layout errors and register-capacity failures remain explicit errors.
This includes migration documentation and the checked Rust compiler allocation/error contract. Numeric package and assembly versions remain with the coordinated release.
Verification
The A-only product sources passed the registered HostAwait regressions, FFI tests, 119 managed tests, and preserved precompiled managed and old C/C++ header consumers. Both VM modes and serialization are covered. Independent Graph/ResourcePolicy and Rust runtime source review found no qualifying implementation defects.
The repository's embedded
no_stdgate passed. The standalonecargo check --no-default-features --features rvm,no_stdconfiguration remainsbroken on current main: the exact comparison produced 62 baseline diagnostics
and 38 with this change, with no new diagnostics. This is a documented existing
configuration limitation, not a passing standalone check.
This PR is independent of the runtime-default changes.