Two key fixes toward closing the IP gap for return_integer.zig: 1. Enable s_in_main_analysis during start.zig comptime evaluation so DECL_VAL resolves declarations lazily. This creates enum_literal and enum_tag entries from evaluating switch/if conditions in start.zig's comptime block (matching the upstream evaluator). 2. Call analyzeMemoizedStateC with memoized_limit=3 (Signedness, AddressSpace, CallingConvention) to create the builtin type entries that the upstream creates through ensureMemoizedStateResolved. 3. Only resolve std/builtin.zig's "builtin" import (CG builtin), not all imports (avoids loading builtin/assembly.zig early). 4. Fix CG builtin namespace collision in ensureNavValUpToDate. CLI output matches reference exactly (217 entries). Test path creates 2 fewer entries due to module loading order difference between CLI and test framework (42:u32 at IP 214 vs reference 216). num_passing stays at 5 pending resolution of this 2-entry test-vs-CLI discrepancy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
About
zig0 aspires to be an interpreter of zig 0.15.2 written in C.
Except for the lexer (written by hand by yours truly), it's been written by an LLM.
The goal of stage0 is to be able to implement enough zig to be able to build
zig1.wasm. For that we need:
- Lexer: DONE, written by hand by yours truly in late 2024.
- Parser: DONE, written mostly by an LLM.
- AstGen: DONE, written fully by an LLM.
- Sema: in progress.
Testing
Quick test:
zig build fmt-zig0 test-zig0
Static analysis (takes a while, run separately):
zig build lint-zig0
More elaborate (tries all compilers + static analysis + ReleaseSafe):
zig build all-zig0 -Doptimize=ReleaseSafe
Most elaborate, takes >10m:
zig build all-zig0 -Doptimize=ReleaseSafe -Dvalgrind |& grep -v Warning
Debugging tips
Test runs infinitely? Build the test program executable:
$ zig build test-zig0 -Dzig0-no-exec
And then run it, capturing the stack trace:
gdb -batch \
-ex "python import threading; threading.Timer(1.0, lambda: gdb.post_event(lambda: gdb.execute('interrupt'))).start()" \
-ex run \
-ex "bt full" \
-ex quit \
zig-out/bin/test
You are welcome to replace -ex "bt full" with anything other of interest.
Float Handling
Float literals are parsed with strtold() (C11 standard, portable). On
x86-64 Linux, long double is 80-bit extended precision (63 fraction bits).
When a float doesn't round-trip through f64, it's emitted as f128 (ZIR
float128 instruction). The 80-bit extended value is converted to IEEE 754
binary128 encoding by bit manipulation — both formats share the same 15-bit
exponent with bias 16383. The top 63 of binary128's 112 fraction bits come
from the 80-bit value; the bottom 49 are zero-padded.
This means float128 literals lose ~49 bits of precision compared to the
upstream Zig implementation (which uses native f128). This is acceptable
because stage0 is a bootstrap tool — the real Zig compiler re-parses all
source with full f128 precision in later stages. The test comparison mask
in astgen_test.zig skips float128 payloads to account for this.
Previous approach used __float128/strtof128 (GCC/glibc extensions) for
full precision, but these are not portable to TCC and other C11 compilers.