Zig Proposal Adds Fil-C-Style Memory Safety at a 1–6x Cost

Khanh Nguyen
Khanh Nguyen
(Updated: )
Listen to this article 0 / 0
Zig and Rust. Credit: bytepith.com

Zig creator Andrew Kelley has filed a proposal to add a new compilation mode to the language, borrowing the runtime-checking techniques behind Fil-C — and pointedly framing it as safer than Rust.

Kelley's Fil ABI Slots a New Runtime Check Layer Onto Zig's Existing Build Modes

Zig already ships four optimization modes: debug and safe (both with safety checks enabled) and fast and small (both without). Kelley's proposal, filed as issue #36237 on Zig's Codeberg tracker, does not touch that list. Instead it adds a separate axis: a new target ABI called "fil," sitting alongside the existing musl, gnu, and msvc ABI options a Zig program can be compiled against.

Selecting the fil ABI would make the compiler implement the same core technique as Fil-C — the memory-safe C/C++ implementation built by Filip Pizlo. That means InvisiCaps, a form of capability metadata attached to every pointer, combined with compiler-level interception of every system call and inline-assembly block so unchecked operations can't slip past the runtime checks. Kelley is explicit that this would be an independent implementation with no code dependency on the Fil-C project itself.

The scope of the claim is what makes the proposal notable: applied to the fil ABI, it would cover not just Zig code but an entire linked tree of C and C++ dependencies, with, in Kelley's words, "no escape hatch." That is a direct jab at Rust's unsafe blocks and its FFI boundary, which the proposal calls out as the place where Rust's compile-time guarantees stop applying in real-world programs that link against C libraries.

The following diagram maps how the new ABI sits relative to Zig's current build modes.

How the proposed fil ABI attaches to Zig's existing build modesDiagram showing Zig's debug, safe, and fast/small optimize modes feeding into a new fil ABI layer that adds InvisiCaps checks and syscall wrapping, producing a binary with no unsafe escape hatch.How the Proposed fil ABI Attaches to Zig's Build ModesBased on Codeberg issue #36237, filed by Andrew Kelley, 20 July 2026Optimize.debugOptimize.safeOptimize.fast / smallNew "fil" ABIInvisiCaps + syscall wrappingx86_64-linux only, so farNo unsafeescape hatchSource: Zig issue #36237, Codeberg

The Proposal's Real Cost: A 1–6x Slowdown, One Supported Platform, No Mixed Linking

Kelley's own text is direct about the tradeoffs, and they are substantial. The performance penalty is stated as roughly 1x to 6x compared to a non-safe build, varying with how much pointer-chasing a given program does — a range wide enough that workloads dominated by pointer-heavy data structures would sit at the costly end.

The fil ABI is also currently scoped to a single target: x86_64-linux. Support for other architectures is described as something that "could be expanded over time," not something the proposal delivers now.

The most operationally significant constraint is linking. The fil ABI is described as incompatible with Zig's other ABIs — every object file in a build must be compiled with fil or the link fails outright, and Zig's linker would need to detect and reject any object that isn't. In practice, that means adopting this mode isn't a flag flip; it requires the entire dependency tree, including any bundled C or C++ libraries, to be rebuilt against the new ABI.

Three constraints defined in the fil ABI proposalThree reference cards showing the proposal's stated performance penalty, current platform support, and linking restriction.Three Constraints Kelley Wrote Into the ProposalAll figures are Kelley's own estimates and stated limits, not independent benchmarksPerformance penalty1–6xvs. non-safe buildPlatform supportx86_64-linuxonly target defined todayABI compatibilityNoneevery linked object needs fil ABISource: Zig issue #36237, Codeberg

Kelley Wants to Skip Fil-C's Garbage Collector — By Tagging Every Object Instead

One detail in the proposal separates it from a straight port of Fil-C rather than a copy of it. Fil-C prevents use-after-free by pairing InvisiCaps with a concurrent garbage collector, FUGC, so that a freed pointer's capability is revoked immediately while the underlying memory is only reclaimed later, once the collector confirms nothing still references it.

Kelley's issue notes that Pizlo told him GC is "just one way" to implement that guarantee, and proposes an alternative: inserting unique type IDs directly into the InvisiCaps metadata. Under that approach, if a use-after-free does occur, the mismatched type tag catches the resulting type confusion without requiring a collector to run at all. Kelley ties this to a proposal already accepted into Zig's roadmap — adding safety checks for pointer casting — arguing the unique-ID groundwork would already be a necessary ingredient for that separate feature.

This is a real design fork, not a footnote. A GC-based approach reclaims memory automatically but adds a background collector's overhead and complexity to a language whose stated design goal is no hidden control flow. A type-tagging approach avoids the collector but leaves open how memory actually gets reclaimed and whether type IDs alone close every use-after-free scenario GC catches by construction.

Two paths to blocking use-after-free without a borrow checkerDecision diagram showing Fil-C's concurrent garbage collector and Kelley's proposed unique-type-ID alternative, both converging on blocking use-after-free and type confusion.Two Paths to Blocking Use-After-FreeKelley's proposal leaves this choice open; Fil-C already picked a sidefree() is calledPath A: Concurrent GCFil-C's current approach (FUGC)capability revoked, memory reclaimed laterPath B: Unique type IDsKelley's proposed alternativetagged into InvisiCaps metadata, no GC requiredUse-after-free blocked;type confusion preventedSource: Zig issue #36237, Codeberg

Calling It "Unlike Rust" Reopened an Old Argument About What "Memory Safe" Means

The proposal's title carries an editorial claim, not just a technical one: an "actually memory safe (unlike Rust)" compilation mode. Kelley's stated reasoning is that real-world Rust programs typically depend on C and C++ libraries reached through FFI, and often use unsafe beyond that boundary too — so a language-level guarantee that never covers those dependencies is, in his framing, incomplete. The proposal concludes that if implemented, Zig would become "one of the only toolchains" able to produce genuinely memory-safe executables once whole C/C++ dependency trees are included.

That framing is what generated the loudest reaction, on Reddit's r/programming and r/rust communities and on Zig's own Ziggit forum, where one thread was split off specifically to separate technical discussion from what its poster called excess "off-topic emotion." The substance of the disagreement is definitional rather than factual: Rust's advocates point out that Safe Rust code, on its own, is checked at compile time with no runtime cost, and that the unsafe boundary is exactly where responsibility is meant to shift to the programmer — a deliberate design choice, not an oversight. Kelley's proposal doesn't dispute that mechanism; it disputes whether it's sufficient once a real program's full dependency graph is considered.

Both claims can be true at once without either side being wrong on the facts: Safe Rust genuinely enforces its guarantees at compile time with zero runtime cost, and a shipped Rust binary genuinely can still contain memory-unsafe code paths through FFI and unsafe blocks that the compiler never checked. What's unresolved — and unlikely to be settled by this proposal alone — is which of those two facts is the more useful definition of "memory safe" for people actually shipping software with C dependencies. It's also unresolved whether a Zig ecosystem still short on the exact kind of large legacy C/C++ codebase Fil-C was built to rescue would see much practical use of the fil ABI, versus reaching for the language's default safety-checked mode instead — a tension that echoed through Bun's own recent shift away from parts of its Zig codebase.

Comparing where memory safety is enforced across three approachesMatrix comparing Safe Rust, the C/C++ status quo, and the proposed fil-ABI Zig mode on where safety is checked, whether an escape hatch exists, and what dependencies require.Where Each Approach Actually Enforces SafetyThree qualitative axes, drawn from the proposal text and each project's own documentationQuestionSafe RustC/C++ status quoProposedfil-ABI ZigWhere safety ischeckedCompile time(borrow checker)Never, by defaultRuntime(InvisiCaps checks)Escape hatchavailableunsafe blocks+ FFIEntire language is unsafeNone — nounsafe keywordDependencyrequirementC libs stay unsafeunless rewrittenNot applicableEvery linked objectrecompiled to fil ABISources: Zig issue #36237; fil-c.org documentation

As of publication, issue #36237 carries no "accepted" label — it's an open proposal from Zig's creator, not a committed roadmap item, and Kelley's own text flags open questions, including whether Fil-C's author would help standardize the fil ABI and whether support could realistically extend past x86_64-linux.

Comments (0)

Sort by:

No comments yet.

Be the first to share your perspective on this topic.