From 382de760bddca4064b37ac63b699e25f01b9660d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Jun 2026 22:05:10 +0000 Subject: [PATCH] P1: fix Zig FFI to build and match the Idris2 ABI The Zig FFI in src/interface/ffi/src/main.zig failed `zig test src/main.zig -lc` under Zig 0.14.0 and one test asserted behaviour the ABI contract does not provide. Errors found: 1. Compile error: `array_list.ArrayListAligned` no longer exposes `popOrNull`. `oblibeniser_undo_pop` called `h.undo_stack.popOrNull()`, which does not exist in Zig 0.14.0. 2. Test failure: the "compute and verify inverse" test finalised the operation with a null (0) post-snapshot pointer, so `post_snapshot` stayed null and `oblibeniser_compute_inverse` correctly returned `not_reversible` (per its contract: an inverse needs both pre- and post-state snapshots). The test asserted `ok`, so it failed. Fixes: 1. Replaced `popOrNull()` with `pop()`, which in Zig 0.14.0 already returns an optional (`?T`); the `orelse` empty-stack handling is unchanged. 2. Updated the test to record with a real pre-state snapshot and finalise with a real post-state snapshot, then assert `ok` for compute_inverse and additionally verify_inverse. This exercises the documented contract instead of weakening the FFI logic. The Idris2 ABI (src/interface/abi/Oblibeniser/ABI/Foreign.idr + Types.idr) remains the source of truth: every `C:` symbol still has a matching `export fn`, and the Result enum integer values (Ok=0 .. InverseProofFailed=7) are unchanged and match resultToInt. Verification: - `zig test src/main.zig -lc` -> all 7 tests pass, zero errors/warnings. - `idris2 --build oblibeniser-abi.ipkg` -> exit 0. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019xMKB3T4Vo5FYC7Czx3JSH --- src/interface/ffi/src/main.zig | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/interface/ffi/src/main.zig b/src/interface/ffi/src/main.zig index a09d422..1711a6d 100644 --- a/src/interface/ffi/src/main.zig +++ b/src/interface/ffi/src/main.zig @@ -444,7 +444,7 @@ export fn oblibeniser_undo_pop(handle: ?*Handle) u64 { return 0; }; - const op_id = h.undo_stack.popOrNull() orelse { + const op_id = h.undo_stack.pop() orelse { setError("Undo stack empty"); return 0; }; @@ -601,10 +601,24 @@ test "compute and verify inverse" { const handle = oblibeniser_init() orelse return error.InitFailed; defer oblibeniser_free(handle); - const op_id = oblibeniser_record_forward(handle, 0, 0); - _ = oblibeniser_finalise_forward(handle, op_id, 0); + // Record with a real pre-state snapshot, then finalise with a real + // post-state snapshot: compute_inverse requires both to be present. + var pre = std.mem.zeroes(StateSnapshot); + pre.state_hash = 0x1111; + var post = std.mem.zeroes(StateSnapshot); + post.state_hash = 0x2222; + + const op_id = oblibeniser_record_forward(handle, 0, @intFromPtr(&pre)); + try std.testing.expect(op_id != 0); + + const finalise = oblibeniser_finalise_forward(handle, op_id, @intFromPtr(&post)); + try std.testing.expectEqual(Result.ok, finalise); + const compute = oblibeniser_compute_inverse(handle, op_id); try std.testing.expectEqual(Result.ok, compute); + + const verify = oblibeniser_verify_inverse(handle, op_id); + try std.testing.expectEqual(Result.ok, verify); } test "undo stack push and pop" {