Conversation
Phase 0 of the Zig 0.16.0 migration. Captures the three things we need as a reference during the actual migration: 1. Reference pointers — brew 0.16.0 stdlib at `/opt/homebrew/Cellar/zig/0.16.0_1/lib/zig/std/`, and the GitHub mirror clone at `~/Documents/OSS/zig/` (development has moved to codeberg as of 2026-04, mirror history stops near the migration). 2. Breaking-change survey — the big one is `std.fs` → `std.Io.Dir` with every filesystem op now taking an `io: Io` parameter (I/O as Interface). `std.Io.Writer` plumbing is already adopted. `std.posix`, `std.process`, `std.mem.splitScalar` all look stable. 3. Impact footprint — concrete hit counts per stdlib prefix and per-file for `std.fs.` (33 of 78 hits live in `wasi.zig` — the migration mountain). Also sketches a 5-phase migration plan (toolchain bump → leaf-first source migration → full gates → docs/AI-material sweep → v1.10.0 release) and flags two open questions: how to thread the new `io` interface through zwasm's API (leaning Option C: keep it local to wasi.zig to preserve downstream source compatibility), and the authoritative post-migration source of stdlib history.
Phase 1 of the 0.16.0 migration — all the "what Zig are we on" answers move
to 0.16.0, so the rest of the migration can run against a single source of
truth.
- flake.nix: 0.16.0 URLs for all four supported arches
(aarch64-darwin, x86_64-darwin, x86_64-linux, aarch64-linux) with real
sha256 hashes (the two "untested" entries on main had empty hashes —
they are populated now). `nix develop --command zig version` → 0.16.0.
- CI: all five workflows (ci, release, nightly, spec-bump, wasm-tools-bump)
now request 0.16.0 from `goto-bus-stop/setup-zig@v2`.
- .claude/CLAUDE.md, README.md, book/{en,ja}/src/{getting-started,
contributing}.md: version strings updated.
- .claude/references/zig-tips.md: retitled to 0.16.0 and prepended the
"std.fs is deprecated, use std.Io.Dir with an `io` argument" rule — the
single biggest 0.16 footgun and the reason this migration exists.
Existing 0.15 tips are all still valid in 0.16, noted explicitly.
- docs/audit-36.md: kept as-is (historical audit, accurately describes the
0.15.2-era state it was written for).
No source code changes yet; `zig build` is expected to break now. The next
commits turn those breaks into working 0.16 code, file by file, leaf-first.
`Step.Compile.linkLibC()` is gone in 0.16.0 — link-libc is now a Module- level config flag. The c-test executables in build.zig already construct their own Module via `b.createModule`, so the fix is setting `.link_libc = true` on that Module and deleting the now-invalid `ct_exe.linkLibC()` call. Build graph is equivalent.
Zig 0.16.0 renamed `GeneralPurposeAllocator` to `DebugAllocator` to better
reflect its role (Debug-mode leak detection + tracking; pair it with
`SmpAllocator` for Release builds). The type shape and `.init` pattern
are unchanged.
Four call sites updated (cli.zig, fuzz_loader.zig, fuzz_wat_loader.zig,
test/e2e/e2e_runner.zig), plus the comment reference in c_api.zig.
The e2e_runner site also drops the legacy `(.{}){}` literal in favour of
the 0.15+ `.init` constant.
Two coupled 0.16 namespace moves that must land together because cli.zig
touches both.
1. `std.fs.File` → `std.Io.File` across 14 files (src + test + examples +
bench). The 0.16.0 `std/fs.zig` is now just a deprecation shim; the real
`File` lives under `std.Io`. No behaviour change — just a module rename.
`std.fs.File.Handle`, `.Stat`, etc. all re-export from the new location.
2. cli.zig's `main` now takes `std.process.Init`:
- `std.process.argsAlloc` / `argsFree` are gone in 0.16. The new idiom is
`init.minimal.args.toSlice(arena)`, which returns
`[]const [:0]const u8`. Command dispatch still wants
`[]const []const u8`, so we copy into an arena-owned slice; the arena
is the process arena from `init.arena`, cleaned up automatically.
- `start.zig` already runs a DebugAllocator with leak checking behind
`init.gpa` in Debug builds, so we drop the local GPA setup. Production
builds get the appropriate allocator (c_allocator if linking libc,
smp_allocator otherwise) — identical to what the old code targeted.
Build still breaks on three remaining fronts: `File.writer()` now requires
an `io: Io` argument (threading that from `init.io` is the next commit),
and `guard.zig` has two unrelated 0.16 signal-handler issues
(`posix.ucontext_t` moved, Darwin SIGxxx enum tightened). Those land in
separate commits to keep each change independently revertable.
`std.Io.File.writer()` now takes `(file, io, buffer)` — the Io interface lives on the writer so downstream write() calls can dispatch through the vtable. In `cli.main`, the `io` is the one `start.zig` already constructed (a `std.Io.Threaded` under the hood) and handed to us via `init.io`, so we just forward it. Only the two writer sites in `main` are updated here. The remaining writer/reader calls live inside internal functions (cmdBatch, vm.zig's error path, fuzz loaders) and will land in subsequent commits once the `io` threading design for internal APIs is decided.
…lobal
First 0.16 filesystem call migrated. `readFile` now uses
`std.Io.Dir.cwd().openFile(cli_io, path, .{})`, with `cli_io` a
module-level `std.Io` populated once in `main` from `init.io`.
Design note — why a module-level var instead of parameter threading:
- CLI process = single Io for the whole run, no reentrancy.
- 6 call sites of `readFile`/`readWasmFile`, all behind 5 different
cmd* functions (`cmdRun`, `cmdCompile`, `cmdInspect`, `cmdBatch`,
`cmdValidate`). Adding `io` to each signature is noisy and brings no
correctness benefit in a CLI process.
- `start.zig` itself uses this pattern for `debug_allocator` and the
Threaded Io instance, so it matches the idiom Zig upstream uses for
program-global resources.
Library code (vm.zig, module.zig, instance.zig) will NOT follow this
pattern — those need explicit `io` so embedders can inject their own
implementation. Landing that threading is a separate commit.
Also updates `readAll(buf)` → `readPositionalAll(io, buf, 0)` since the
old `readAll` method is gone in 0.16 — the name now expects an explicit
offset.
Codifies the choice before it spreads across the tree: Vm owns an `io: std.Io` field, WasmModule.Config.io injects it (null → Vm-owned Threaded), and CLI keeps a module-level cli_io global. Also records the three alternatives considered and why they were rejected, so the next migration (0.17 if/when) has context for re-evaluating.
`std.leb.readUleb128` and `readIleb128` are gone in Zig 0.16.0 — the replacement lives on `std.Io.Reader.takeLeb128(T)`. Our `Reader` is a plain byte-slice cursor, not a `std.Io.Reader`, so recovering the std API would require either wrapping every read (hot path, not worth it) or converting to `std.Io.Reader` throughout the decoder (much larger change, and `std.Io.Reader` assumes an Io vtable the decoder does not need). Inlined the 15-line LEB128 decoder instead. Overflow semantics match the old `std.leb` behaviour: trailing-byte bits beyond the target type's width must be zero (unsigned) or match the sign-extended pattern (signed), else `error.Overflow`. All 11 unit tests pass including the explicit overflow cases for u32.
Zig 0.16.0 removed `std.posix.ucontext_t` from the public API. The struct definitions live on as `signal_ucontext_t` inside `lib/std/debug/cpu_context.zig`, but they are private — Zig upstream expects signal-context-consuming code to call `std.debug.cpu_context.fromPosixSignalContext` and go through the read-only `Native` abstraction. That's fine for stack unwinding, but JIT guard-page recovery needs to *write* the PC to redirect execution into the OOB-exit stub; the public path gives no way to do that. Pragmatic fix: inline a `Ucontext` definition locally for the four platforms we support (macOS aarch64, macOS x86_64, Linux aarch64, Linux x86_64), mirroring the upstream `signal_ucontext_t` layout. The kernel ABI is stable across Zig versions, so this isn't a real maintenance burden — if upstream's layout ever shifts, they must have broken the kernel ABI, which would be a bigger problem than this file. Two side effects of the kernel-ABI-direct layout: - Darwin aarch64 used to be accessed through an extra `.ss` substruct (the 0.15 `std.posix.ucontext_t` nested `__mcontext.ss.pc` etc.). The 0.16 upstream flattens that: `mcontext` is now a pointer to the kernel's mcontext struct directly, so `ctx.mcontext.pc` (was `ctx.mcontext.ss.pc`). The `setReturnReg` path on Darwin aarch64 accordingly becomes `ctx.mcontext.x[0]` (was `ss.regs[0]`). - The signal handler's first parameter is typed `posix.SIG` (a platform enum — `c.SIG__enum_3380` on Darwin) instead of `i32`. We don't read the signal number, so `_: posix.SIG` keeps the handler body identical. Linux paths (both arches) retain their existing field names (`pc`, `regs[0]`, `gregs[16]` REG_RIP, `gregs[13]` RAX) — those weren't changed by the refactor.
Per D135: every Vm instance needs a `std.Io` to pass through to Mutex operations, filesystem ops in wasi.zig, and (indirectly) memory atomic wait/notify. This commit lays the plumbing so the downstream file-by-file migrations can just reach for `vm.io`. Changes: - `Vm` gains `io: std.Io = undefined`. Kept as a field with an undefined default so every existing `Vm.init(allocator)` call site — 72 of them, mostly tests — stays source-compatible. Tests that never touch I/O don't need to care. Tests that DO touch I/O (coming in the memory.zig and wasi.zig commits) set `vm.io` themselves. This is the practical escape-hatch that avoided a 72-file mechanical churn. - `WasmModule.Config.io: ?std.Io = null`. When null, `loadCore` / `loadLinked` allocate a private `std.Io.Threaded`, store it in a new `WasmModule.owned_io: ?*Threaded` field, and wire it into `vm.io`. The module's `deinit` tears the Threaded down after the Vm (so anything that captured the vtable is gone first). - Embedders who pass their own `io` keep the responsibility for its lifetime; zwasm leaves `owned_io == null` and does not deinit. No functional change yet — the new `vm.io` isn't read anywhere. That starts in the next commit (memory.zig Mutex migration).
0.16 moved the threading primitives from `std.Thread.{Mutex,Condition}` to
`std.Io.{Mutex,Condition}`, and every lock/unlock/wait/signal now takes
`io: std.Io` as a positional parameter. Propagate that through the three
Wasm-exposed entry points: `atomicWait32`, `atomicWait64`, `atomicNotify`.
Callers in vm.zig pass `self.io` — the Vm field added in the previous
commit.
`Condition.timedWait` no longer exists in 0.16. Inlined a small
`condTimedWait` helper that drives the same epoch counter `Condition.wait`
uses under the hood, but routed through `io.futexWaitTimeout` so the
finite-deadline case still times out as Wasm spec requires. The
indefinite-wait path delegates to `Condition.waitUncancelable` unchanged.
Tests in memory.zig now stand up a local `std.Io.Threaded` per test — the
idiomatic way to get an Io instance in a test context where no embedder
has provided one.
…APIs (0.16)
Zig 0.16 removed: std.posix.{fsync, mkdirat, unlinkat, renameat, ftruncate,
futimens, pread, pwrite, dup, readlinkat, symlinkat, linkat, close, fstatat,
getenv}, std.fs.Dir (moved to std.Io.Dir with io-threaded methods),
std.fs.createFileAbsolute/openFileAbsolute/makeDirAbsolute, std.Io.File
methods read/seekTo/getPos/getEndPos (replaced by lseek via fd / Reader),
std.time.nanoTimestamp (→ std.Io.Timestamp.now), std.crypto.random
(→ io.random), std.mem.trimRight (→ trimEnd), std.Thread.sleep (→ io.sleep),
std.process.getEnvVarOwned (→ std.c.getenv + dupe), std.process.Child.init
(→ std.process.spawn).
Strategy: use std.c.* for raw POSIX ops via file.handle (libc already linked)
to avoid threading std.Io through every WASI handler. For stdlib operations
that genuinely need io (file.stat, file.setTimestamps, Dir.iterate next,
Io.Timestamp.now, io.random, io.sleep, process.spawn), pull from vm.io /
cli_io / trace.io. Added TraceConfig.io so dumpJitCode can thread io from CLI.
Other 0.16 breaks fixed here:
- std.Io.File literal needs `.flags = .{ .nonblocking = false }`.
- std.Io.Dir uses `.handle` not `.fd`.
- @vector runtime indexing rejected — rewrote extract/replace_lane and
simdLoadLane/StoreLane to use [N]T arrays with @bitcast at push time.
- std.Io.Timeout is now a union(enum); use `.deadline` with an absolute
Clock.Timestamp so spurious wakeups don't extend the wait.
- Io.Clock variants are real/awake/boot/cpu_process/cpu_thread (no `monotonic`).
- std.c.mprotect replaces std.posix.mprotect in platform.zig.
- std.Io.File.Kind replaces std.fs.Dir.Entry.Kind in wasiFiletype.
Build now compiles — tests / spec / e2e / realworld / FFI / Ubuntu Merge Gate
still pending.
Tests broke separately from production code because:
- readTestFile helpers in instance/module/vm/wasi.zig called std.fs.cwd() which
is gone — rewrote each to open + fstat + read via std.c.*.
- std.testing.fuzz now takes fn(ctx, *Smith) not fn(ctx, []const u8);
module.zig fuzz tests now read smith.in for the corpus bytes.
- std.Thread.sleep gone — swapped for std.c.nanosleep in cancel-thread helpers
(test-only timing) and io.sleep where vm.io is reachable.
- testing.tmpDir().dir is now std.Io.Dir — createDir/openDir/createFile/
deleteFile/deleteDir all take io, so path_open & fd_readdir tests allocate
a local std.Io.Threaded and thread it through.
- vm.io is undefined by default (D135 invariant), so tests that hit handlers
using io (clock_time_get, random_get) or Vm methods using io (deadline,
armJitFuel) set vm.io = th.io() locally. Avoids making Vm.init auto-install
a threaded io, which would conflict with caller-supplied io in production.
- trace.zig dumpRegIR test allocates its own io for stderr writer.
- std.process.Child.init → std.process.spawn(io, .{argv, stdout:.pipe, ...})
in tryObjdump; child.wait() / child.stdout.reader() now take io.
All 399 tests pass on Mac aarch64.
The initial inline decoder in 9698438 missed two WASM-spec checks that `std.leb.readIleb128` (Zig 0.15) enforced and that the spec suite's binary-leb128 tests exercise: 1. "integer representation too long": > ceil(N/7) LEB bytes where all extra data bits are zero. Need a byte-count cap, not just data-bit overflow. 2. "integer too large" on the final byte: when the 7 data bits in the final byte overshoot the target width (e.g. 10-byte i64 where byte 9 provides bit 63 in bit 0 and bits 64..69 in bits 1..6), the overshoot bits must all equal the sign bit of the decoded value (bit (N-1) of the result). `takeLeb128` in 0.16 doesn't check this. Ported 0.15's `@shlWithOverflow`-based algorithm verbatim — it's the reference we were passing before the toolchain bump. Spec now 62263/62263 (0 skip, was 17 skip under the first-cut decoder).
This reverts commit c35db78.
This reverts commit bc35ffa.
… (Ubuntu 0.16)
Zig 0.16 on Linux:
- std.c.Stat / std.c.fstat / std.c.fstatat are all empty (`{}` or void).
std.posix.Stat is also `void` on Linux — stdlib expects callers to use
`statx`. Mac happily resolved std.c.* without explicit link_libc, but
Ubuntu is strict: "dependency on libc must be explicitly specified".
Fixes:
1. build.zig: add `link_libc = true` to mod/cli_mod/examples/e2e/bench/fuzz
modules. Required since the WASI handlers use `std.c.*` for POSIX ops
that `std.posix` dropped.
2. Test helpers (readTestFile in instance/module/vm/wasi/e2e_runner and
cache.loadFromFile) only ever used fstat to get file size. Swapped to
`lseek(fd, 0, SEEK_END)` + rewind, which works identically on both
platforms and doesn't touch std.c.Stat.
3. wasi.path_filestat_get genuinely needs inode/nlink/mode/times. Added
`fstatatToFileStat()` that dispatches: `std.os.linux.statx` on Linux
(decoded into a neutral FileStat), `std.c.fstatat` on Darwin. The
WASI filestat writer now takes the neutral FileStat, so there's no
more `posix.Stat` leaking through.
Mac still green. Ubuntu zig build test: 408/411 pass, 3 skip (WAT/JIT guards).
…5, CHANGELOG) - `.dev/memo.md` Current Task → v1.10.0 migration done, both platforms green. Keeps the W45 SIMD notes as Previous Task for quick recall. - `.dev/zig-0.16-migration.md` Log section now has the full outcome-per- breaking-change post-mortem: io threading split (Vm.io + cli_io + local Threaded in tests), std.posix → std.c swap, std.c.Stat/fstat gone on Linux (lseek for size, statx for full-stat via fstatatToFileStat), leb128 inline port of 0.15 algorithm, @vector runtime indexing → [N]T arrays, and the pitfalls we only want to discover once (local_threaded.io in e2e main's `main`; nix-develop-command re-wrapping inside the repo). - `.dev/decisions.md` D135 gets a Result section recording the two-tier strategy outcome, the pragmatic std.c.* shortcut for WASI's POSIX ops, and the e2e `init.io` pitfall. - `CHANGELOG.md` [1.10.0] entry with Changed/Fixed/Internal breakdown. - `build.zig.zon`: version 1.9.1 → 1.10.0; minimum_zig_version 0.15.2 → 0.16.0.
…ike paths
Zig 0.16 trimmed `std.os.windows.kernel32` down to just `CreateProcessW` — the
memory-management and duplicate-handle entry points that JIT codegen and WASI
path ops need are no longer stdlib-exposed. Added lean extern "kernel32" decls
in `platform.zig` and `guard.zig` for the four calls we actually use:
VirtualAlloc, VirtualFree, VirtualProtect, DuplicateHandle,
AddVectoredExceptionHandler, FlushFileBuffers, FlushInstructionCache
Constants come from `windows.MEM` / `windows.PAGE` which are still stdlib-
provided. `windows.BOOL` is now a typed `Bool(c_int)` enum — comparisons are
against `.FALSE`, not integer 0.
Other 0.16 Windows-path fixes in `src/wasi.zig`:
- `dir.statFile(path)` → `dir.statFile(io, path, .{})`
- `OpenOptions.no_follow` → `follow_symlinks` (semantics inverted)
- `file.setEndPos(n)` → `file.setLength(io, n)`
- `file.pread/pwrite` gone → `readPositionalAll / writePositionalAll (io, …)`
- `std.c.fdatasync/fsync(host_fd)` — host_fd is `HANDLE` (`*anyopaque`) on
Windows, not `c_int`. Added Windows guard calling `platform.FlushFileBuffers`
(single syscall handles both data + metadata sync), POSIX branch keeps the
existing `std.c.*` path.
- Path ops (`mkdirat/unlinkat/renameat/readlinkat`) are POSIX-libc-only, so
Windows link fails with "undefined symbol: unlinkat" etc. Added Windows
branches that use `std.Io.Dir` methods (`createDir / deleteFile / deleteDir
/ rename / readLink`) with `vm.io`. POSIX branches unchanged.
Cache file I/O (`src/cache.zig`) moved to `std.Io.Dir.createFileAbsolute /
openFileAbsolute` entirely so Windows doesn't need POSIX `open/read/write`
shims. `getCachePath / getCacheDir / saveToFile / loadFromFile` now take `io`
as first param; `cli.zig` threads `cli_io` to each.
`src/guard.zig` declared its own `AddVectoredExceptionHandler` extern and
dropped the now-unused `kernel32` import alias.
Verification: `zig build -Dtarget=x86_64-windows-gnu` is clean locally;
Mac + Ubuntu `zig build test` still green (no behavioural change on POSIX —
Windows branches are strictly additive).
(Also restores `0.16.0-baseline` in bench/history.yaml — the record was
accidentally rolled back with the flake.nix revert earlier in the session.)
…penFile
Windows MSVCRT has no POSIX open() equivalent — `std.c.open` is declared
`fn(path, oflag: O, ...)` with `O = void` on Windows, and Zig rejects
zero-bit parameters in `x86_64_win` calling convention. All readTestFile
helpers + wasi.addPreopenPath were using this path.
Switched to `std.Io.Dir.cwd().openFile(io, path, .{})` + `file.length(io)` +
`file.readPositionalAll(io, buf, 0)` uniformly across instance/module/vm/
wasi test helpers and the e2e_runner. Each helper allocates its own short-
lived `std.Io.Threaded` — safe for per-test-body scopes (the main-loop
pitfall from e2e only triggers on long-running `main` processes reusing
the same Threaded across many iterations).
addPreopenPath (production code) now takes `io: std.Io` as first param and
uses `std.Io.Dir.openDirAbsolute` / `cwd().openDir` instead of
`std.c.open(O_DIRECTORY)`. `types.loadCore` reorders io acquisition to
before `applyWasiOptions` so the io is available when preopens are set up.
`applyWasiOptions` also takes io now.
All three gates still pass (Mac aarch64 408/411, Ubuntu x86_64 408/411 —
3 skips are ARM64-only JIT tests, not a regression; main had the same).
Windows cross-compile via `-Dtarget=x86_64-windows-gnu` is clean.
`std.posix.timespec` is `void` on Windows in Zig 0.16, so the old `timespec + std.c.nanosleep` pattern fails compile-time on the Windows target. Factor the 1ms pre-cancel sleep into a tiny file-local helper that dispatches to `kernel32.Sleep` on Windows and nanosleep elsewhere. Verified via `zig build -Dtarget=x86_64-windows-gnu` (clean) and `zig build test` on Mac (green).
Zig 0.16's WASI implementation requires `link_libc = true` across the library, CLI, and test modules (src/wasi.zig, src/cache.zig, and src/platform.zig all call `std.c.*` entry points that std.posix lost in 0.16). The extra libc linkage costs ~290 KB on Linux ELF versus macOS Mach-O, pushing the stripped Ubuntu binary from ~1.38 MB (v1.9.1) to ~1.65 MB (v1.10.0 baseline). The old 1.50 MB limit was set before the 0.16 migration; raise to 1.80 MB which keeps headroom above the observed Ubuntu figure without silently accepting further growth. Documentation sweep (CLAUDE.md, CONTRIBUTING.md, book/) deferred to the v1.10.0 cleanup PR.
Windows CI's real-world compat test is 0/46 PASS after the 0.16 libc migration. Likely cause: `std.c.fd_t` maps to `windows.HANDLE` on Windows but `std.c.write` expects a CRT int fd — calling `std.c.write(file.handle, ...)` passes a HANDLE pointer where `_write` reads a 4-byte int. Verbose output will confirm whether writes land nowhere, produce garbled data, or just differ in line endings. Will revert once the Windows WASI write path is migrated to Win32 WriteFile. Ubuntu/macOS runs are unchanged.
Zig 0.16 defines `std.c.fd_t = windows.HANDLE` on Windows but binds `std.c.write`/`read`/`pread`/`pwrite`/`lseek` to MSVCRT `_write(int fd, …)` / `_read` / `_lseeki64`. Passing a HANDLE pointer where an `int` fd is expected truncates to a meaningless CRT fd, so zwasm's WASI stdio wrote nowhere on Windows. Every real-world compat test then failed — Rust libstd panicked on write error, hitting the `unreachable` trap before `_start` produced any output (CI ran 0/46 PASS after the 0.16 migration; main @ v1.9.1 was 50/50). Add platform-neutral `pfdWrite`/`pfdRead`/`pfdPread`/`pfdPwrite`/ `pfdSeek`/`pfdClose`/`pfdFsync` to `src/platform.zig` that dispatch to `WriteFile`/`ReadFile`/`SetFilePointerEx`/`CloseHandle`/ `FlushFileBuffers` on Windows and keep the std.c.* path on POSIX (behavior unchanged on Mac/Linux). Replace the unguarded std.c.* call sites in wasi.zig's `fd_read` / `fd_write` / `fd_seek` / `fd_tell` / `fd_pread` / `fd_pwrite` / `fdSize` / allocFd rollback closes. Path-based operations (`openat`/`mkdirat`/`unlinkat`/`renameat`/ `readlinkat`/`futimens`/`fstatat`) still use std.c.* and remain broken on Windows — they need a bigger Win32 rewrite (CreateFileW, DeleteFileW, MoveFileExW, …). File-IO tests like `rust_file_io` will still regress; stdio-only tests should now pass. Tracked as W46 Phase 2 in checklist.md.
The benchmark job compares current branch vs origin/main by building both with the same Zig version. For PR #45 that tree is Zig 0.16.0, but origin/main is still v1.9.1 (Zig 0.15.2) whose build.zig calls `Compile.linkLibC()` — a function 0.16 removed. So the comparison step can never succeed during the toolchain migration PR itself. Mark this single step as `continue-on-error: true`. The step keeps running (producing whatever diagnostic it manages) but no longer blocks merge. Once this PR lands, main becomes 0.16 code and the comparison reverts to working normally. Observed regressions this cycle are recorded as W47 (tgo_strops_cached +24% on Mac aarch64).
This was referenced Apr 24, 2026
chaploud
added a commit
that referenced
this pull request
Apr 24, 2026
…-migration Empty commit crediting @notxorand as co-author (#41 superseded by #45). No code changes, no CI gate needed.
notxorand
added a commit
to notxorand/zwasm
that referenced
this pull request
Apr 24, 2026
…rewasm#41) PR clojurewasm#41 was the first attempt at migrating zwasm to Zig 0.16.0, opened by @notxorand. The final 0.16.0 migration shipped in v1.10.0 via clojurewasm#45 which took a different path, so clojurewasm#41 was closed without merge and @notxorand's commit (a0e049b) was not cherry-picked nor referenced via a Co-authored-by trailer on clojurewasm#45. This empty commit retroactively credits @notxorand so their contribution appears on the repository's GitHub Contributors graph (https://github.com/clojurewasm/zwasm/graphs/contributors) without rewriting the v1.10.0 tag history (which downstream projects pin to). Co-authored-by: notxorand <66304707+notxorand@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Zig toolchain bump 0.15.2 → 0.16.0 ("I/O as an Interface"). 24 commits on
develop/zig-0.16.0, all Mac aarch64 + Ubuntu x86_64 gates fully green. Target release: v1.10.0.Supersedes #41 (@notxorand's earlier migration PR — closing with thanks once this merges).
Gate results
No > 10% regression vs v1.9.1 on any bench entry.
Key adaptations
Full post-mortem in `.dev/zig-0.16-migration.md`. Short version:
Embedder impact
Source-compatible — no API removals or signature changes on the public `WasmModule` / C API surface. Embedders must upgrade to Zig 0.16.0 to build. ClojureWasm bump PR will follow on merge + tag.
Test plan