Skip to content

Compiler hardening – C signature contract and pointer bindings

October 11, 2026 – merged to unstable, not in a tagged release. Two compiler corrections landed on the development branch: the C/Zig foreign signature contract (PR #34, merge a2780174, Gaps 195, 201 and 202) and borrowing through let pointer bindings (PR #40, merge a600b784, Gap 205 and review finding F-91). No version was bumped and no release is announced.

C signature contract – Gaps 195, 201, 202

Section titled “C signature contract – Gaps 195, 201, 202”
  • Unnamed C parameters keep their arguments. int abs(int); now behaves like int abs(int value);. Previously the type-only parameter was skipped during signature extraction and its call argument was lost, so libc.abs(-7) printed an unrelated number. restrict pointer parameters keep their pointer type.
  • Variadic calls are refused with E2413. A call to a variadic C function such as printf, through graft c, use zig or graft zig, fails before code generation with a located error naming the callee. Previously it compiled with a fixed-parameter ABI. The declaration itself stays allowed, and fixed functions from the same header remain callable.
  • Variadic function pointers are refused with E2413. A call to a foreign function whose parameter or return type is a pointer to a variadic function fails the same way, because Janus func(...) values carry only fixed-arity calling conventions.

Migration: call a variadic function through a fixed C-ABI adapter. See C Header Grafts: The Signature Contract and Language Reference §11.3.1.

  • Borrowing through a let binding works. let root = &doc.nodes[0] followed by &root.children[i] was rejected as a write to root for structs from another module, such as parsed std.encoding.kdl nodes. It now compiles and runs, including chained indexing (&board.grid[i][j]).
  • Writes through the binding stay refused, at every index depth after a field. Before the repair, board.grid[i][j] = v through a let binding escaped the check that board.grid[i] = v enforced.
  • A silent write loss is fixed. A chained write such as boards[1].grid[1][0] = 5 could land in a temporary and vanish.

See Language Reference §4.1.1 and the tutorial Borrowing Through Pointer Bindings. The Monastery mission 05-borrowed-address/01-borrow-through-a-binding teaches the same rules.

  • Merge gates recorded for both merges: the full compiler test suite on the integration tree whose trees equal the merge trees, and native red-to-green regressions: ./scripts/zb test-c-header-discovery and ./scripts/zb test-index-borrow.
  • Documentation examples: every Janus example in the pages linked above was compiled with janus build and run on Linux x86_64, profile :core, with a compiler built from integration tree 6f2bbb06 (unstable at a600b784 plus an unrelated KDL fix). Its embedded version string still reports v2026.9.2-alpha (356d36a); the source revision above is the identity that matters.
  • Janus does not implement the C variadic calling convention. E2413 is a refusal, not support.
  • Writes through a var pointer binding are lost (Gap 208, open). Write through an edit pointer parameter or the owning value.
  • Chained indexing through a slice is wrong (Gap 223, open). For rows: [N][]T or [][]T, bind the inner slice first. No workaround is verified for a slice of arrays ([][N]T).
  • A let binding is not a read-only view. cell.* = v and index stores on an array-pointer binding compile and write. Only an explicit view parameter rejects field mutation (E2702), and it does not yet cover .* stores through derived addresses (Gap 187, open).
  • Aggregate C ABI support is unchanged.