Zig as an Intermediate Language for EiffelStudio
- Tags:
- dbc
- eiffelstudio
- eiffel
- software engineering
- code generation
- design by contract
- open source
- class invariants
- runtime performance
- systems programming
- Zig
- intermediate language
- compiler backend
- Melting Ice Technology
- incremental compilation
- LLVM
- C backend
- Andrew Kelley
- Zig Software Foundation
- cross compilation
- native compilation
- compiler design
- C macros
- comptime
- undefined behaviour
- frozen code
- finalised code
- bytecode interpreter
- virtual dispatch
- devirtualisation
- whole program optimisation
- zig cc
- .NET backend
- CLR
- assertion levels
- preconditions
- postconditions
- real time systems
- nonprofit
- programming language design
- Contents
- Preface
- Introduction
- Prior Art and Community Context
- The C Backend: A Structural Assessment
- The .NET Backend as Proof of Concept
- Why Zig Rather Than LLVM IR Directly
- The Macro Problem: A Significant Technical Challenge
- Zig's Incremental Compilation: The Central Argument
- The Zig Project: Credibility and Sustainability
- Implementation Considerations and Risks
- A Proposed Path Forward
- Conclusion
- See Also
- References and Notes
A Case for Replacing the C Backend — Discussion Paper for Review by Eiffel Software Engineering
(20-25 minutes: 5000 words)
Preface
This paper was inspired by watching the interview ''Zig 2026: No-AI Policy, $670K Foundation, Left GitHub & Why Zig Isn't 1.0 - Andrew Kelley Explains'', published 27 May 2026, in which Zig's creator Andrew Kelley describes — among other things — the incremental compilation milestone his team had just reached: sub-50-millisecond rebuilds of million-line codebases using Zig's own native x86 backend. That single claim prompted a discussion about what such a capability might mean for EiffelStudio's Melting Ice compilation model, and this paper grew out of that discussion.
The technical arguments were developed collaboratively between a practising Eiffel developer and an AI assistant (Claude, Anthropic). The Eiffel developer provided domain knowledge, real-world experience of the frozen/finalised divergence problem, and editorial direction throughout. The AI contributed structure, prose, and a number of the technical observations. The result is what it is: a plausible and reasonably well-informed argument, but one that should be reviewed by Eiffel Software engineers before being treated as authoritative. Factual corrections are actively welcomed.
Introduction
EiffelStudio has long used C as its primary intermediate language — the mechanism by which Eiffel source code becomes native executables. This was a sound choice when first made. C compilers are available on virtually every platform, and targeting C allowed EiffelStudio to achieve portability without building and maintaining a separate native code generator for each architecture. The approach is pragmatic and time-tested.
However, the C backend also carries structural costs that have become increasingly visible over decades of use. The three-tier Melting Ice compilation model — with its fundamental split between development and production behaviour — exists largely as a workaround for the slowness and opacity of invoking a C compiler as a subprocess. Frozen and finalised executables differ in behaviour in ways that are difficult to diagnose. Contract-heavy code written to Eiffel's Design by Contract philosophy runs at a significant penalty during development, encouraging developers to disable the very mechanisms that make Eiffel trustworthy.
This paper argues that Zig — a modern systems programming language with a mature and growing compiler infrastructure — represents a compelling candidate to replace or augment C as EiffelStudio's intermediate language. The argument draws on four pillars: the precedent established by the existing .NET backend; Zig's architectural properties as a compilation target; the specific advantages Zig's incremental compilation model offers for Eiffel's development workflow; and the credibility and sustainability of the Zig project itself.
Prior Art and Community Context
The idea of using Zig as a backend for Eiffel did not originate with this paper. It has been circulating in the Eiffel community since at least 2024, and one implementation has already quietly shipped. This section acknowledges that prior work and situates this paper's contribution within it.
John Wolter's 2024 Proposal
In September 2024, John Wolter posted to the eiffel-users mailing list under the subject Zig can work as an Eiffel backend, describing early experiments using Zig as an additional Eiffel backend using open source tooling. He identified the lack of a formal object-oriented system in Zig as a challenge, but noted that Zig's language features might allow OO translation to happen nonetheless, and pointed to Zig's cross-compilation capabilities and support for diverse emerging hardware targets as opening new markets for Eiffel. That post attracted 50 views but limited follow-up discussion — the community was not quite ready to engage with it at the time. It deserves recognition as the seed of the idea.
Eric Bezault's Practical Validation
The most significant prior contribution comes from Eric Bezault (Gobo Eiffel), who in
February 2024 reported to the eiffel-users list that he had successfully used zig cc
as a backend C compiler for the Gobo Eiffel compiler. His observation is worth quoting
directly, as it is the most important single piece of evidence for the feasibility of
Stage 1 of the path proposed in this paper:
zig cc is built on
top of clang. So if EiffelStudio works with clang, it should be possible to use Zig as well. | Eric Bezault, eiffel-users, February 2024This is not speculation — it is a statement from an Eiffel compiler expert who had already
done it. The clang lineage of zig cc means that any Eiffel compiler already
supporting clang as a backend C compiler can adopt zig cc with minimal friction.
Eric also independently arrived at the same zero-install vision that Andrew Kelley describes
as his gold standard: download, unzip, compile, with no further configuration or environment
variables required. He credits Paul Cohen for originally bringing the Zig project to his
attention.
By February 2026, Eric's Gobo Eiffel package was shipping with Zig toolchain integration as a standard part of the distribution, with users already experiencing faster subsequent compilations as a result. Stage 1 of the path proposed in this paper is therefore not a proposal — it is an existence proof. The question for EiffelStudio is whether to follow where Gobo has already gone.
What This Paper Adds
What the earlier discussions did not attempt, and what this paper tries to provide, is a detailed technical case: why the C backend's structural costs matter, what Zig's properties mean specifically for Design by Contract and the Melting Ice model, what the macro layer challenge involves and how Zig addresses each use case, and what a credible staged path from the current state to a full Zig backend might look like. The technical argument rests on prior community work and is now supported by Andrew Kelley's 2026 description of Zig's incremental compilation milestone. The present paper attempts to connect those dots.
The C Backend: A Structural Assessment
The C backend has served EiffelStudio well, but understanding its limitations requires examining not just what it does, but the architectural constraints it imposes on the entire compilation pipeline.
The Three-Tier Problem
EiffelStudio's Melting Ice Technology divides compilation into three modes: melting, freezing, and finalising. Each mode exists to serve a different goal — speed of iteration, debuggable native code, and optimised production output respectively. The elegance of the metaphor — frozen ice, melted drops — should not obscure that this three-tier model is fundamentally a consequence of using C as the backend.
Melting is fast because it bypasses C compilation entirely, running changed code through a bytecode interpreter. This achieves fast iteration at the cost of running two execution models simultaneously: recently changed code is interpreted while unchanged code runs as compiled native binary. The resulting execution environment is not the same as a deployed system.
Freezing generates C and invokes the C compiler, producing a debuggable native binary. However, the C compiler used at freeze time does not perform the whole-program optimisation that finalisation performs. Devirtualisation, dead feature elimination, cross-class inlining — these are unavailable to the freeze-time C compiler because it does not have visibility across the entire system at once.
Finalisation performs a global analysis pass before emitting C, enabling these whole-program optimisations. The resulting binary runs substantially faster — sometimes matching hand-written C++ — but is not debuggable, because the optimisations it applies destroy the structural correspondence between source lines and machine instructions that a debugger requires.
The practical consequence is that developers cannot work with the same binary they will ship. They debug in an environment that runs differently from production, and they measure performance in a build that cannot be debugged. This is not a minor inconvenience — it is a fundamental separation between development and deployment that opens a gap in which bugs can hide.
Frozen and Finalised Behavioural Divergence
In practice, experienced EiffelStudio developers know that unit tests must be run against finalised code, because frozen and finalised builds can behave differently. This divergence is rare but when it occurs it is extremely difficult to diagnose. The developer cannot attach a debugger to the build exhibiting the problem, and the build that accepts a debugger does not exhibit the problem.
The root causes are several. Finalisation's dead feature elimination may incorrectly determine that a feature is unreachable, removing code that frozen builds retain. Devirtualisation at finalisation time may incorrectly resolve a polymorphic dispatch, inlining the wrong concrete method. Most fundamentally, the generated C may contain patterns that constitute undefined behaviour under the C standard — patterns that a conservatively optimising freeze-time compiler tolerates silently, but that an aggressively optimising finalise-time compiler exploits in ways that change observable behaviour.
The Design by Contract Penalty
Eiffel's Design by Contract — preconditions, postconditions, and class invariants — is the language's most distinctive contribution to software engineering. It is the mechanism by which Eiffel programs can provide correctness guarantees that are enforced at runtime, not merely stated in documentation.
The C backend creates a practical dilemma for DbC during development. Class invariants that walk data structures on every call, or postconditions that perform non-trivial verification, impose runtime costs that are substantial in melted and frozen builds — where virtual dispatch overhead is not eliminated, where inlining does not collapse contract calls into inline comparisons, and where the interpreter adds its own overhead on top. The result is that developers routinely reduce assertion levels during development to make the system responsive, re-enabling contracts only for final validation.
This is a significant compromise. Consider a real-time audio or playback system where timing constraints are hard. In melted mode the system may simply be too slow to demonstrate correct behaviour under real-time conditions. The developer must disable contracts to hit the timing budget, and in doing so removes the safety net precisely when it is most needed — when exploring new code under production-like stress.
The .NET Backend as Proof of Concept
Before examining Zig's properties as a target, it is worth observing what the existence of EiffelStudio's .NET backend demonstrates about the team's capabilities and the compiler's architecture.
Targeting the .NET Common Language Runtime required solving a set of deeply non-trivial problems. The CLR has a rich, opinionated type system; mapping Eiffel's class hierarchy and multiple inheritance model onto CLR types required careful design. The CLR manages memory through a garbage collector the Eiffel runtime does not control; reconciling Eiffel's own memory semantics with the CLR's required significant engineering. .NET generics are constrained in ways that Eiffel's parametric polymorphism is not. The CLR's exception model uses structured exception handling; Eiffel's rescue and retry mechanism had to be expressed in terms of a fundamentally different exception architecture.
Solving these problems required the EiffelStudio team to decouple Eiffel's semantics from assumptions baked into the C backend — to identify what was fundamental to the language and what was an artefact of one particular implementation strategy. This decoupling is precisely the prerequisite for targeting a second backend. The .NET backend did not merely add a compilation target; it forced and validated an architectural separation between the language's semantics and its implementation.
Targeting LLVM IR — which Zig uses as one of its own backends — is arguably less demanding than targeting .NET. LLVM IR is minimal and low-level; it has almost no opinions about type systems, memory models, or calling conventions. A team that successfully mapped Eiffel onto the CLR has already demonstrated the harder conceptual capability.
Why Zig Rather Than LLVM IR Directly
Given that LLVM IR is an ultimate compilation target anyway, one might ask why Zig should be the intermediate language rather than targeting LLVM IR directly. The answer lies in the practical advantages Zig offers beyond what LLVM IR alone provides.
Readability and Debuggability of Generated Code
LLVM IR, while more readable than machine code, is not designed for human inspection. Zig is. Generated Zig code, while not what a human programmer would write by hand, is readable, structured, and checkable. When the code generator has a bug — and code generators always have bugs — the intermediate representation can be inspected and understood by an engineer familiar with Zig. The existing C backend provides this property to some extent, but C's undefined behaviour makes generated C treacherous to reason about. Zig's explicit treatment of unsafe operations makes generated Zig considerably more trustworthy as an artefact for human inspection.
No Undefined Behaviour
The frozen/finalised behavioural divergence discussed earlier is largely a consequence of C's undefined behaviour. Zig does not have this category of problem. Operations that would be undefined in C — signed integer overflow, out-of-bounds access, use of uninitialised memory — are either detected at runtime in debug builds or must be explicitly opted into. A code generator targeting Zig cannot accidentally produce UB that is silently tolerated in development and exploited in release.
Cross-Compilation as a First-Class Feature
Zig's cross-compilation story is substantially better than C's. Zig ships with its own libc implementations for a wide range of targets and can cross-compile to any supported target from any host with a single compiler invocation. For EiffelStudio, which targets Windows, macOS, Linux, FreeBSD, and other platforms, this would simplify build infrastructure considerably. Notably, Uber already uses zig cc to cross-compile Go code with C dependencies for ARM64 targets — a practical validation of this capability at production scale.
The zig cc Stepping Stone
An important practical point: adopting Zig does not require immediately rewriting the code generator. Zig ships a C compiler frontend — invocable as zig cc — that is a drop-in replacement for clang, with full cross-compilation support. EiffelStudio could adopt zig cc as the C compiler used during freeze and finalise as an immediate first step, gaining cross-compilation benefits without any changes to the code generator itself.
The Macro Problem: A Significant Technical Challenge
Any honest assessment of a Zig backend for EiffelStudio must confront a substantial technical obstacle: EiffelStudio's generated C relies heavily on C macros, and Zig has no macro system. This is not a minor compatibility issue — the macro layer is load-bearing in the current architecture.
What the Macro Layer Actually Does
EiffelStudio's use of C macros in generated code is not accidental — each use case was well-motivated. Macros abstract over platform differences: a type alias such as EIF_INTEGER expands to the appropriate integer type for the target architecture, with the C preprocessor resolving the variation before the compiler sees it. Macros implement virtual dispatch table access, allowing the exact structure of type descriptors to be tuned per platform. Macros control assertion levels at compile time. Macros implement garbage collector write barriers — inlined at every reference assignment. And macros paper over calling convention differences and alignment requirements across the platforms EiffelStudio targets.
In aggregate, the macro layer functions as a second code generation step performed by the C preprocessor for free.
What Zig Offers Instead
Zig has a deliberate answer to every macro use case, and in each case the answer is architecturally cleaner — but it requires the code generator to do more work explicitly.
Platform portability, currently handled by type-aliasing macros, is addressed in Zig through comptime and the builtin target query system. Virtual dispatch tables become explicit Zig struct definitions with function pointer fields. Assertion level control maps naturally onto Zig's builtin.mode — a compile-time value representing the optimisation tier — allowing contract checks to be gated on comptime conditionals that the compiler eliminates entirely in release builds. Garbage collector write barriers become Zig inline fn declarations — a genuine language guarantee of inlining, unlike C macros, and with full type-system participation.
The Real Implication: The Code Generator Must Be Smarter
The absence of macros in Zig means every platform variation currently resolved by the preprocessor must instead be resolved by the code generator itself. What the code generator emits is what Zig sees — there is no second pass.
This is genuinely more work. The implicit platform knowledge encoded in the macro layer — accumulated over decades and distributed across header files — must be made explicit. However, this constraint contains an opportunity. EiffelStudio's C macro layer is essentially undocumented tribal knowledge, encoding platform portability logic, GC integration details, dispatch mechanisms, and contract handling in a form that is difficult to read, impossible to type-check, and invisible to debuggers. Moving to Zig forces all of that implicit knowledge into explicit, typed, debuggable code.
The macro layer was a solution to 1990s problems: unreliable C compiler availability, unstable portable type definitions, untrustworthy inline functions. Zig eliminates each of these problems at the language level.
Zig's Incremental Compilation: The Central Argument
The most compelling argument for Zig as EiffelStudio's intermediate language is Zig's incremental compilation architecture and what it would mean for the Melting Ice model.
The 50ms Claim: From the Founder's Own Words
This is not speculation. In a 2026 interview, Andrew Kelley — Zig's creator — described the capability directly:
Kelley explained the architectural reason this is possible: Zig is in the process of ditching LLVM for its own native backends, which it describes as removing a core product dependency. By owning the entire compilation pipeline, Zig can implement true incremental compilation — patching object code in-place rather than recompiling. This is categorically different from what Melting Ice achieves. Melting Ice achieves fast iteration by abandoning native compilation for changed code. Zig's incremental compilation achieves fast iteration while retaining it.
Collapsing the Three Tiers into One
If EiffelStudio targeted Zig and took advantage of Zig's incremental compilation, the three-tier Melting Ice model could collapse into a single development build. There would be no need for a distinct melt mode running interpreted bytecode, because the native build would already rebuild in milliseconds. There would be no frozen/finalised divergence, because there would be one code generation path with one set of optimisation decisions. The binary a developer runs and debugs would be the same binary — at a lower optimisation level — as the binary shipped to users.
This is not a marginal improvement. It resolves the fundamental structural problem with EiffelStudio's compilation model — that development and production are different worlds — without sacrificing either iteration speed or execution correctness.
There is also an important interaction with the debugger. Modern LLVM-based debug info (DWARF) is sophisticated enough that meaningful debugging is possible at moderate optimisation levels. A development build using Zig could be moderately optimised — enough that contracts are not cripplingly slow — while retaining good line-level debugger fidelity. That middle ground barely exists in EiffelStudio's three-tier model today.
Design by Contract Restored
If incremental native compilation makes development builds fast enough that full contract checking is affordable throughout the development cycle, developers no longer face a choice between responsive development and contractual correctness. Preconditions, postconditions, and class invariants can remain enabled continuously — catching violations at the earliest possible moment, in the normal flow of development rather than only in finalised test runs.
For a real-time system, a development build at moderate optimisation with full contracts might well meet the timing requirements that an interpreted melted build cannot, while still rebuilding in milliseconds after a change. The contracts would be doing their job throughout the development process.
The Zig Project: Credibility and Sustainability
An argument for depending on Zig requires confidence that Zig will exist and be maintained over the multi-year horizon of a backend project. The evidence here is reassuring.
Financial Independence
The Zig Software Foundation is a US 501(c)(3) nonprofit — not a startup, not VC-backed. In 2024 its total income was $670,000, of which 91% went directly to paying the five contractors who work on the project. Kelley is the foundation's sole employee, drawing a salary of $154,000 — the median senior software engineer salary in New York City at the time the nonprofit was formed. The income is deliberately diversified across many individual donors and companies, so that no single sponsor can exert leverage. As Kelley put it: we can turn to any individual sponsor and say I'm sorry but we will not do what you say and if you take your money away we will survive.
When asked whether he would accept a hypothetical $100 million donation, Kelley said he would take it — but not grow. He would put it in the bank and never fundraise again for 100 years. This is not a project chasing growth or an exit. It is run as a long-term craft.
Community Reception
Zig is consistently ranked in the top five most admired programming languages in developer surveys. Notable production users include Ghostty (a terminal emulator by Mitchell Hashimoto), TigerBeetle (a financial transaction database that achieves a thousand-fold performance advantage over Postgres-based OLTP by using Zig's predictable allocation model), Bun (a JavaScript runtime, now owned by Anthropic), and Uber (which uses zig cc for cross-compiling Go code with C dependencies). This is not a toy language — it is in production at serious organisations.
The No-AI Policy: A Signal About Code Quality
Zig has a strict no-LLM, no-AI policy for contributions. Kelley's reasoning is worth understanding: AI-generated pull requests are invariably garbage — they consume limited reviewer time, teach the contributor nothing, and will never lead to core team membership. The policy is also framed as part of Zig's educational mission: the project exists partly to develop skilled systems programmers, and AI contributions undermine that goal.
This policy is relevant to EiffelStudio for a different reason: it signals that the Zig codebase and its contributors are held to a high standard of genuine understanding. A project that makes this choice about its own contributors is likely to make careful decisions about language design as well.
Moving to Codeberg
In late 2025, Zig moved its main repository from GitHub to Codeberg after GitHub's continuous integration became unreliable. Codeberg is a German nonprofit hosting service. This move caused some community discussion about whether it would harm adoption, but Kelley's view is clear: I don't think that people are discovering Zig through GitHub or Codeberg. I think they're discovering it through talks, meetups, YouTube videos. The move is consistent with the project's preference for nonprofit infrastructure and independence from corporate platforms.
Implementation Considerations and Risks
Zig Is Not Yet at Version 1.0
Zig's most recent release at the time of writing is 0.16 (beta). The language's incremental compilation with its own x86 backend is arriving in this release cycle, but full feature coverage across all targets is still in progress. Targeting an unstable language as an intermediate representation carries the risk of breaking changes requiring ongoing maintenance of the code generator.
This concern is genuine but bounded. Kelley has stated that when 1.0 arrives, it will be a true uncompromising labor of love — we will not have to be stuck with any bad decisions that we had to rush to lock in. The core language semantics are substantially stable already. A multi-year backend project would likely complete after 1.0 is tagged.
The Existing C Backend Investment
Decades of engineering work are embedded in EiffelStudio's C code generator, runtime, and garbage collector integration. A Zig backend would not replace this overnight. The practical approach would be incremental: the zig cc adoption path requires no changes to the code generator. A Zig backend could initially target a well-defined subset of Eiffel, with the C backend retained as reference and fallback throughout.
Community and Ecosystem Size
Both Eiffel and Zig are languages with smaller communities than C, Rust, or Go. A dependency between two smaller ecosystems concentrates risk. This risk is partially mitigated by LLVM as a fallback — a Zig backend for EiffelStudio would have the option of targeting LLVM IR directly if Zig itself became problematic.
A Proposed Path Forward
Rather than framing this as a binary choice, a staged approach distributes risk while capturing early benefits.
- Stage 1 — Adopt zig cc as the C compiler
Replace the C compiler invoked during freeze and finalise with
zig cc. No changes to the code generator. Immediate gain in cross-compilation capability and a basis for evaluating Zig tooling.
- Stage 2 — Prototype Zig code generation for a subset
Implement a Zig code generator for a well-defined subset of Eiffel — perhaps flat classes without agents or generics — and validate output correctness against the C backend. At this stage, explicitly map each macro use case to its Zig equivalent and document the code generator requirements. Evaluate the development experience with Zig's incremental compilation on this subset.
- Stage 3 — Extend to full Eiffel feature set
Progressively extend the Zig backend to cover agents, generics, expanded types, and the full rescue/retry mechanism. Maintain the C backend as reference and fallback throughout.
- Stage 4 — Collapse the Melting Ice tiers
Once the Zig backend achieves feature parity and Zig's incremental compilation is stable across targets, restructure the development build to use Zig's incremental native compilation in place of the melt/freeze distinction. Evaluate whether the finalise tier can be simplified or whether LLVM-level optimisation should remain as a release-only path.
Conclusion
The C backend has served EiffelStudio well, but it imposes architectural costs that are not incidental — they are structural consequences of using C as a compilation target. The three-tier Melting Ice model, the frozen/finalised behavioural divergence, and the Design by Contract performance penalty are all downstream effects of the same root cause: that C compilation is slow, that slow compilation forces the bytecode interpreter shortcut, and that the shortcut creates two execution worlds that should be one.
Zig addresses this root cause directly. Its incremental native compilation architecture — delivering 50-millisecond rebuilds of real compiled code across million-line codebases — offers development-cycle performance that makes the bytecode shortcut unnecessary. For Eiffel, a language whose identity is built on correctness guarantees enforced at runtime, the ability to run full contracts continuously in fast native code is not a convenience. It is a restoration of the language's central promise.
The existence of the .NET backend demonstrates that EiffelStudio's architecture can support multiple code generation targets, and that the team has already solved the harder conceptual problems involved in mapping Eiffel semantics onto a foreign intermediate representation. Targeting Zig is technically less demanding than targeting the CLR was.
The absence of macros in Zig is a genuine implementation challenge, but it forces something valuable: all the implicit knowledge encoded in the current macro layer must be made explicit, typed, and debuggable. The resulting backend would be substantially more transparent and maintainable than the current C backend with its header-defined macro vocabulary.
The Zig Software Foundation is financially independent, ideologically committed to long-term quality over short-term growth, and producing a language that is already in production at organisations including Uber and Anthropic. These are not the characteristics of a risky bet.
The question is not whether Zig's properties would benefit EiffelStudio — they plainly would — but whether the timing and resources make this a practical investment. The staged approach outlined above makes it tractable, beginning with zero changes to the code generator and progressing incrementally. The long-term benefits to EiffelStudio's development experience, and to the integrity of Design by Contract as a living practice rather than a release-time formality, justify beginning that investment now.
See Also
- EiffelStudio — the IDE and compiler this paper concerns
- Melting Ice Technology — EiffelStudio's incremental compilation system
- Zig Programming Language — official website
- Andrew Kelley interview — source of the 50ms incremental compilation claim and foundation financial details
References and Notes
- Incremental compilation timing claim: Andrew Kelley, video interview, 2026. Direct quote used in the body of this article.
- Zig Software Foundation finances: 2024 annual report, published on the Zig blog.
- EiffelStudio compilation modes: Melting Ice Technology documentation on eiffel.org.
- This article should be reviewed for accuracy regarding EiffelStudio's internal C code generation, the precise role of macros in generated output, and the current state of the .NET backend. The authors welcome corrections from Eiffel Software engineers.
