Skip to content

Commit a4a72a2

Browse files
P1: fix Zig FFI null-handle test under Zig 0.14.0 (#31)
## The bug `zig test src/main.zig -lc` failed on `test "error handling — null handle"` (exit code 1). The test invokes `bqniser_type(null, 0)` and then asserts `bqniser_last_error() != null` — i.e. that calling an accessor with a null handle records the null-pointer error condition, consistent with the ABI's `NullPointer` result code (`NullPointer = 4` in `src/interface/abi/Bqniser/ABI/Types.idr`). The function was the bug, not the test. `bqniser_type`'s null-handle and uninitialized-handle paths returned `0` silently: ```zig const h = castHandle(handle) orelse return 0; // no setError -> last_error stays null if (!h.initialized) return 0; ``` So `last_error` was never populated and the assertion failed. (The prior `lifecycle` test ends in `bqniser_free`, which calls `clearError()`, so `last_error` is null entering the null-handle test.) ## The fix On the null/uninitialized paths of the accessor functions that previously swallowed the error (`bqniser_type`, `bqniser_bound`, `bqniser_to_f64`), call `setError(...)` to record the condition — the same house idiom already used by `bqniser_eval`, `bqniser_call1`, `bqniser_call2`, and `bqniser_read_f64_arr`, and matching the worked exemplar in `alloyiser` where the null-handle path maps to the ABI's null-pointer condition. This makes the null-handle path observably report the error, consistent with the ABI's `NullPointer` semantics. The test's expectation was correct and is unchanged; only the implementation was corrected. ## ABI preserved - No exported C symbol names changed; every `C:<name>` in `Foreign.idr` still has a matching `export fn <name>`. - The `Result` enum integer values are unchanged and still match `resultToInt`: `ok=0, error=1, invalid_param=2, out_of_memory=3, null_pointer=4, eval_error=5`. - Only `src/interface/ffi/src/main.zig` was touched. ## Verification - `cd src/interface/ffi && zig test src/main.zig -lc` → **All 4 tests passed** (zero errors/warnings). - `cd src/interface/abi && idris2 --build bqniser-abi.ipkg` → **exit 0** (build dir removed afterwards). - ABI symbol ↔ Zig export parity confirmed; Result values match `resultToInt`. Note: any rust-ci / Hypatia / governance red checks are pre-existing estate-infra issues unrelated to this Zig-only change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019xMKB3T4Vo5FYC7Czx3JSH --- _Generated by [Claude Code](https://claude.ai/code/session_019xMKB3T4Vo5FYC7Czx3JSH)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent d29c797 commit a4a72a2

1 file changed

Lines changed: 24 additions & 6 deletions

File tree

src/interface/ffi/src/main.zig

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,29 @@ export fn bqniser_call2(handle: ?*anyopaque, f: BQNV, w: BQNV, x: BQNV) BQNV {
191191

192192
/// Get the type tag of a CBQN value.
193193
export fn bqniser_type(handle: ?*anyopaque, val: BQNV) u32 {
194-
const h = castHandle(handle) orelse return 0;
195-
if (!h.initialized) return 0;
194+
const h = castHandle(handle) orelse {
195+
setError("Null handle in bqniser_type");
196+
return 0;
197+
};
198+
if (!h.initialized) {
199+
setError("Handle not initialized");
200+
return 0;
201+
}
196202
_ = val;
197203
// TODO: return @intCast(bqn_type(val))
198204
return 0;
199205
}
200206

201207
/// Get the element count (bound) of a BQN array.
202208
export fn bqniser_bound(handle: ?*anyopaque, val: BQNV) u64 {
203-
const h = castHandle(handle) orelse return 0;
204-
if (!h.initialized) return 0;
209+
const h = castHandle(handle) orelse {
210+
setError("Null handle in bqniser_bound");
211+
return 0;
212+
};
213+
if (!h.initialized) {
214+
setError("Handle not initialized");
215+
return 0;
216+
}
205217
_ = val;
206218
// TODO: return bqn_bound(val)
207219
return 0;
@@ -230,8 +242,14 @@ export fn bqniser_read_f64_arr(handle: ?*anyopaque, val: BQNV, buf: BQNV) Result
230242

231243
/// Extract a scalar f64 from a BQN numeric value.
232244
export fn bqniser_to_f64(handle: ?*anyopaque, val: BQNV) f64 {
233-
const h = castHandle(handle) orelse return 0.0;
234-
if (!h.initialized) return 0.0;
245+
const h = castHandle(handle) orelse {
246+
setError("Null handle in bqniser_to_f64");
247+
return 0.0;
248+
};
249+
if (!h.initialized) {
250+
setError("Handle not initialized");
251+
return 0.0;
252+
}
235253
_ = val;
236254
// TODO: return bqn_toF64(val)
237255
return 0.0;

0 commit comments

Comments
 (0)