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 likeint abs(int value);. Previously the type-only parameter was skipped during signature extraction and its call argument was lost, solibc.abs(-7)printed an unrelated number.restrictpointer parameters keep their pointer type. - Variadic calls are refused with E2413. A call to a variadic C function
such as
printf, throughgraft c,use zigorgraft 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.
Pointer bindings – Gap 205 and F-91
Section titled “Pointer bindings – Gap 205 and F-91”- Borrowing through a
letbinding works.let root = &doc.nodes[0]followed by&root.children[i]was rejected as a write torootfor structs from another module, such as parsedstd.encoding.kdlnodes. 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] = vthrough aletbinding escaped the check thatboard.grid[i] = venforced. - A silent write loss is fixed. A chained write such as
boards[1].grid[1][0] = 5could 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.
Verification
Section titled “Verification”- 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-discoveryand./scripts/zb test-index-borrow. - Documentation examples: every Janus example in the pages linked above was
compiled with
janus buildand run on Linux x86_64, profile:core, with a compiler built from integration tree6f2bbb06(unstableata600b784plus an unrelated KDL fix). Its embedded version string still reportsv2026.9.2-alpha (356d36a); the source revision above is the identity that matters.
Remaining limits
Section titled “Remaining limits”- Janus does not implement the C variadic calling convention. E2413 is a refusal, not support.
- Writes through a
varpointer binding are lost (Gap 208, open). Write through aneditpointer parameter or the owning value. - Chained indexing through a slice is wrong (Gap 223, open). For
rows: [N][]Tor[][]T, bind the inner slice first. No workaround is verified for a slice of arrays ([][N]T). - A
letbinding is not a read-only view.cell.* = vand index stores on an array-pointer binding compile and write. Only an explicitviewparameter rejects field mutation (E2702), and it does not yet cover.*stores through derived addresses (Gap 187, open). - Aggregate C ABI support is unchanged.