Zig ships pointer stability locks for ArrayList, reworks packaging

Zig's devlog for the week covers three separate changes to the language's standard library and toolchain. First, Robbie Lyman writes that Pointer Stability Locks, a technique already used since 2024 to guard std's Hash Map containers, has now been extended to std.ArrayList through a pull request Leo Emar-Kar originally opened in 2025. The problem it solves: code that stores a pointer or slice into an ArrayList's backing memory can end up with a dangling reference the moment the list grows and reallocates, since ArrayList, unlike HashMap, is ordered and can also move elements around during calls like orderedRemove() or pop() without reallocating at all. Lyman walks through a worked example where a Context struct holds one ArrayList of raw bytes and a second ArrayList of string slices that point into the first; parsing two chunks of input in sequence corrupts the stored slices once the first list resizes, producing garbled output instead of a clean test failure. Calling ctx.history.lockPointers() before storing the pointers, then unlockPointers() once they are no longer needed, turns that silent corruption into an explicit panic with a stack trace pointing at the exact call that violated the lock. Second, Andrew Kelley describes moving all package-management functionality, the zig build, zig fetch, zig init and zig libc subcommands, out of the compiler executable and into the build system's own process, which he calls the maker process. Large parts of what used to ship inside the compiler binary now ship as source instead, including the package-fetching logic, the HTTP client and networking stack, TLS and its crypto, the Git protocol, and compression libraries for xz, gzip, zstd, flate and zip. Because the maker executable is compiled in ReleaseSafe mode, package management now runs with safety checks enabled during networking that the compiler itself does not carry, and the crypto used for networking and file hashing can take advantage of CPU-specific instructions too rare to depend on in a distributed compiler binary. The change shrinks the compiler's own executable by 4%, from 14.1 to 13.5 MiB in a no-LLVM ReleaseSmall build. Kelley's original motivation was enabling a build server protocol to unblock the ZLS language server after an earlier process split into separate configurer and maker processes had broken the --build-runner override flag; with maker now the parent of configurer rather than its sibling, a long-running zig build --watch process can keep the maker process alive across configuration reruns instead of tearing it down and restarting a client connection. Kelley calls the change almost entirely non-breaking, though the --maker-optflag flag is replaced by a ZIG_DEBUG_MAKER environment variable and --zig-lib-dir by ZIG_LIB_DIR. He lists four follow-up issues, a build server protocol MVP, path dependencies for the build script itself, watch mode detecting changes to the build script, and a cache-miss bug tied to the working directory, as the main blockers before Zig 0.17.0 is tagged, and says that with two conferences coming up in July he realistically will not have time to finish them until early August; he thanks Techatrix of the ZLS team for help on the build server protocol. Third, Ali Cheraghi reports on the SPIR-V backend, which had bitrotted after recent compiler changes and needed several weeks of work to bring back into a better state. A new @SpirvType builtin now lets code express SPIR-V-specific types, such as samplers, images and runtime arrays, that don't otherwise fit Zig's type system, closing a long-standing blocker for writing shaders. Execution mode information, like workgroup size and fragment origin, is now carried on the calling convention itself rather than emitted through inline OpExecutionMode assembly, which the SPIR-V assembler now rejects if written manually; two new calling conventions, spirv_task and spirv_mesh, were added for mesh shading pipelines. Capabilities and extensions, previously emitted ad hoc, are now derived from the target's CPU feature set the same way other backends work, with dependency chains pulled from SPIRV-Headers. Cheraghi also describes starting to move SPIR-V codegen, which had run single-threaded inside the linker thread since the backend's inception, onto the same multi-threaded Mir-based pipeline every other self-hosted backend already uses.

Key facts

  • std.ArrayList gains Pointer Stability Locks (lockPointers/unlockPointers), the technique std's Hash Map has used since 2024, via a PR Leo Emar-Kar originally opened in 2025; it turns silent dangling-pointer corruption into an explicit panic with a stack trace.
  • Andrew Kelley moved the zig build, fetch, init and libc subcommands, plus the networking, TLS, Git protocol and compression code they depend on, out of the compiler and into the build system's maker process, shrinking the compiler binary 4% from 14.1 to 13.5 MiB (no-LLVM ReleaseSmall build).
  • The move gives package management ReleaseSafe-mode safety checks during networking and lets its crypto use rare CPU instructions; it was motivated by unblocking a build server protocol for the ZLS language server, and Kelley expects to finish the remaining blockers for the Zig 0.17.0 tag around early August.
  • Ali Cheraghi's SPIR-V backend work adds an @SpirvType builtin for shader-only types, moves execution mode data onto the calling convention with two new callconvs (spirv_task, spirv_mesh), derives capabilities and extensions from CPU features, and begins moving codegen off a single thread.

Why it matters

All three items chip away at long-standing rough edges in Zig's toolchain rather than adding user-facing features. Pointer stability locks close a real memory-safety gap: ArrayList reallocation can silently corrupt pointers into it, and the bug is normally hard to trace back to its cause. Moving package management out of the compiler is a structural change that makes the compiler binary smaller and lets package fetching, networking and crypto be patched, rebuilt and safety-checked independently of the compiler itself, while also clearing the way for a build server protocol that other tools, like the ZLS language server, depend on. The SPIR-V work matters to anyone using Zig to write GPU shaders or compute kernels, since @SpirvType and the calling-convention changes close gaps that previously made certain shader patterns impossible to express.

Who it affects

Zig developers who store pointers or slices into an ArrayList's backing memory, and want a clear panic instead of silent corruption when the list resizes. Contributors and tool authors who touch Zig's build system or package manager, since large parts of that logic now ship as patchable source in the maker process rather than being baked into the compiler; this includes the ZLS team, whose build server protocol work depends on the process restructuring. Zig users writing SPIR-V shaders or GPU kernels, who gain new type and calling-convention primitives from Ali Cheraghi's backend work.

How to use it

For ArrayList pointer safety, call list.lockPointers() before storing a pointer or slice into the list's backing memory, and list.unlockPointers() once those references are no longer needed; any resize, or an orderedRemove() or pop() call that moves elements, will then panic with a stack trace instead of silently corrupting the stored pointer. For the build system change, two flags have moved to environment variables: --maker-optflag is now ZIG_DEBUG_MAKER, and --zig-lib-dir is now ZIG_LIB_DIR; Kelley describes the rest of the change as almost entirely non-breaking. For SPIR-V, the @SpirvType builtin declares shader-specific types like samplers and images directly, and execution mode (workgroup size, fragment origin, and so on) is set through the function's calling convention, for example callconv(.spirv_vertex) or the new callconv(.{ .spirv_task = .{ .x = 1, .y = 1, .z = 1 } }) for mesh shading, rather than through manual OpExecutionMode assembly, which the assembler now rejects.

How solid is it

This is an official Zig project devlog written by the developers who did the work (Robbie Lyman, Andrew Kelley and Ali Cheraghi), with code listings, a reproduced bug, and an actual panic stack trace included for the ArrayList change. The package management restructuring is described as already done and shipped to the maker process, with a measured 4% binary size reduction; Kelley frames it as almost entirely non-breaking. The SPIR-V work is presented as an ongoing recovery from a bitrotted state, with the @SpirvType builtin and calling-convention changes already landed and multi-threaded codegen described as a change in progress rather than finished.

Risks and caveats

Kelley lists four specific follow-up issues, a build server protocol MVP, path dependencies for the build script, watch-mode detection of build-script changes, and a working-directory cache-miss bug, as blockers before Zig 0.17.0 can be tagged, and says he realistically will not get back to them until early August because of two conferences in July. No exact calendar date is given for any of the three devlog entries beyond those relative references. The devlog does not say what specific recent compiler changes caused the SPIR-V backend to bitrot in the first place.

“We can have AOT cake and eat JIT, too!”

— Andrew Kelley, on moving Zig's package manager into the build system