From 56ca68d54be9720d329eceb33137b3ebc5b68b2a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Jun 2026 22:04:35 +0000 Subject: [PATCH] P1: fix Zig FFI to build and match the Idris2 ABI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Zig FFI (src/interface/ffi/src/main.zig) failed to compile under Zig 0.14.0 with two errors, both in functions reached from betlangiser_init / sampleDistribution: 1. betlangiser_init: `@bitCast(std.time.nanoTimestamp())` was assigned to the u64 PRNG seed, but nanoTimestamp() returns i128 — a @bitCast size mismatch (128 -> 64 bits). Fixed by reinterpreting the i128 as u128 and truncating to u64: `@truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))`. 2. sampleDistribution (Box-Muller normal sampling): local variables `u1` and `u2` shadowed the built-in primitive type `u1`, which is an error in Zig 0.14. Renamed to `unif1`/`unif2`. Both fixes are local and behaviour-preserving. No exported C symbol names, signatures, or integer encodings changed: all 26 `C:betlangiser_*` symbols in the Idris2 ABI (Foreign.idr) still map to matching `export fn`s, the Result enum (ok=0..sampling_failed=6) still matches Types.idr resultToInt, the TernaryBool encoding (t_false=0, t_true=1, t_unknown=2) matches ternaryToInt, and the DistributionTag encoding (normal=0..custom=4) matches distributionTag. The Idris2 ABI remains the source of truth and was left untouched. Verification: - `zig test src/main.zig -lc` -> all 9 tests pass, zero errors/warnings. - `idris2 --build betlangiser-abi.ipkg` -> exit 0 (build dir removed). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019xMKB3T4Vo5FYC7Czx3JSH --- src/interface/ffi/src/main.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interface/ffi/src/main.zig b/src/interface/ffi/src/main.zig index 97280ac..2c03a08 100644 --- a/src/interface/ffi/src/main.zig +++ b/src/interface/ffi/src/main.zig @@ -115,7 +115,7 @@ export fn betlangiser_init() ?*anyopaque { engine.* = .{ .allocator = allocator, .initialized = true, - .prng = std.Random.DefaultPrng.init(@bitCast(std.time.nanoTimestamp())), + .prng = std.Random.DefaultPrng.init(@truncate(@as(u128, @bitCast(std.time.nanoTimestamp())))), .distributions = std.ArrayList(*Distribution).init(allocator), }; @@ -555,9 +555,9 @@ fn sampleDistribution(engine: *Engine, dist: *const Distribution) f64 { return switch (dist.tag) { .normal => blk: { // Box-Muller transform for normal sampling - const u1 = random.float(f64); - const u2 = random.float(f64); - const z = @sqrt(-2.0 * @log(u1)) * @cos(2.0 * std.math.pi * u2); + const unif1 = random.float(f64); + const unif2 = random.float(f64); + const z = @sqrt(-2.0 * @log(unif1)) * @cos(2.0 * std.math.pi * unif2); break :blk dist.param1 + dist.param2 * z; }, .uniform => dist.param1 + (dist.param2 - dist.param1) * random.float(f64),