Technology
Zig v0.17.0
Key Points
Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software. Zig development is funded via Zig Software Foundation, a 501(c)(3) non-profit organization. Please consider a recurring donation so that we can offer more billable hours to our core team members.
Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
Zig development is funded via Zig Software Foundation, a 501(c)(3) non-profit organization. Please consider a recurring donation so that we can offer more billable hours to our core team members. This is the most straightforward way to accelerate the project along the Roadmap to 1.0. If you need donation receipts or are looking to migrate away from GitHub Sponsors, we recommend donating via Every.org.
This release features 5 months of work: changes from 206 different contributors, spread among 925 commits.
Originally predicted to be shorter, this release cycle ended up substantial, with the Build System reworked, including the introduction of the Build Server Protocol, and the ELF Linker enhanced to the point where we expect Incremental Compilation to work for everyone on x86_64-linux.
@bitCast
changes@backingInt
and @fromBackingInt
@SpirvType
@divCeil
@hasDecl
Returns true
Only for Public Declarationscomptime
Length Slicesvoid{}
Syntax Removederrdefer
Capture Removedi0
Removedinternal
and link_once
Global Linkage Removeddebug.SafetyLock
Gains Support for Shared Lockingfmt.allocPrint
moved to mem.Allocator
std.zon.parse
Reworkedbit_set
Variants and Deprecate the Managed Onestd.lang.Type
lang.OptimizeMode
to lang.Optimize
lang.Optimize.runtimeSafety
mem.eql
and mem.findDiff
Uri
and net.HostName
Step.Options
: add addOptionPathDirectoryZig supports a wide range of architectures and operating systems. The Support Table and Additional Platforms sections cover the targets that Zig can build programs for, while the zig-bootstrap README covers the targets that the Zig Compiler itself can be easily cross-compiled to run on.
Notable changes:
aarch64-openbsd
is now tested natively in Zig's CI, ensuring high-quality
support going forward.aarch64-freebsd
and aarch64-netbsd
CI jobs now run on pull
requests too, in addition to master
pushes.aarch64-windows
binaries, including the Zig Compiler, has been
worked around.aarch64-openbsd
so that the resulting binaries actually work.loongarch32-linux-gnu[sf]
targets has been added.sparc64-linux
. This is largely thanks to Zig's new ELF linker which now has better
support for this target than LLD.aarch64-switch
,
arm-gba
, mipsel-psx
, and powerpc-wiiu
xtensa-linux
support has been added to Zig. Note that, for now,
this support can only be exercised via the C backend or the experimental LLVM
backend.arc[eb]-linux
,
csky-linux
, and m88k-openbsd
when using the C backend.microblaze[el]-linux
,
sh[eb]-linux
, and sparc-linux
.-mabi=ieeelongdouble
for all PowerPC targets. This is just a
formalization of what was already reality; Zig has never supported the IBM "double-double"
format for long double
and likely never will. As a result, this release drops
support for powerpc-linux-gnueabi[hf]
because glibc only supports the "double-double"
format on these targets. The powerpc-linux-musleabi[hf]
targets remain supported as
they use the IEEE format.powerpc64-linux-gnu
. Zig has only ever
supported linking ELFv2 binaries for 64-bit PowerPC, and glibc does not officially support
ELFv2 on big endian - nor IEEE long double
, as above.aarch64-haiku
: cortex_a55
m68k-*
: M68030
mips64-openbsd
: octeon
powerpc-netbsd
: 750
powerpc64-freebsd
: pwr8
powerpc64-linux
: pwr8
powerpc64-openbsd
: pwr9
s390x-*
: arch11
sparc-*
: generic
sparc-linux
: v9
sparc64-*
: ultrasparc
xtensa-*
: esp32
Zig's level of support for various targets is broadly categorized into four tiers with Tier 1 being the highest. The goal is for Tier 1 targets to have zero disabled tests - this will become a requirement for post-1.0.0 Zig releases.
In the following table, ✅ indicates full support, ❌ indicates no support, and ⚠️ indicates that there is partial support, e.g. only for some sub-targets, or with some notable known issues. ❔ indicates that the status is largely unknown, typically because the target is rarely exercised. Hover over other icons for details.
Targets marked with 🪦 are obsolescent; the Zig compiler and standard library maintain best-effort support for them, but that support is expected to be removed eventually.
| Tier | Target | Code Gen. | Linker | Lang. Feat. | Std. Lib. | Stack Traces | Fuzzer | libc | CI |
|---|---|---|---|---|---|---|---|---|---|
| 1 | x86_64-linux |
🖥️⚡ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
|||||||||
| 2 | aarch64-freebsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | aarch64[_be]-linux |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | aarch64-maccatalyst |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | aarch64-macos |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | aarch64[_be]-netbsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | aarch64-openbsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | aarch64-windows |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | arm-freebsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | arm[eb]-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | arm[eb]-netbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | arm-openbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | hexagon-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | loongarch32-linux |
🖥🛠 | ✅ | ❔ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | loongarch64-linux |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | mips[el]-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | mips[el]-netbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | mips64[el]-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | mips64[el]-openbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | powerpc-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | powerpc-netbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | powerpc-openbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | powerpc64[le]-freebsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | powerpc64[le]-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | powerpc64-openbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | riscv32-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | riscv32-netbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️ |
| 2 | riscv64-freebsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️ |
| 2 | riscv64-linux |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | riscv64-netbsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️ |
| 2 | riscv64-openbsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 | s390x-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | sparc64-linux |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | thumb[eb]-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | wasm32-wasi |
🖥️🛠️ | ✅ | ✅ | ✅ | ⚠️ | ❌ | ✅ | ✅ |
| 2 | x86-linux |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | x86-netbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | x86-openbsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ |
| 2 🪦 | x86-windows |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | x86_64-freebsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 🪦 | x86_64-maccatalyst |
🖥️⚡ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️ |
| 2 🪦 | x86_64-macos |
🖥️⚡ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️ |
| 2 | x86_64-netbsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 2 | x86_64-openbsd |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| 2 | x86_64-windows |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
|
|
|||||||||
| 3 | aarch64-haiku |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 | aarch64-ios |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 | aarch64-serenity |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 | aarch64-tvos |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 | aarch64-visionos |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 | aarch64-watchos |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 | arm-haiku |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 | mips64[el]-netbsd |
🖥️ | ✅ | ✅ | ✅ | ❌️ | ✅ | ❌️ | ❌️ |
| 3 | riscv64-haiku |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 | riscv64-serenity |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 🪦 | thumb-windows |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌️ |
| 3 | wasm64-wasi |
🖥️🛠️ | ✅ | ❔ | ❌️ | ⚠️ | ❌ | ❌️ | ❌️ |
| 3 🪦 | x86-freebsd |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
| 3 | x86-haiku |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 🪦 | x86-illumos |
🖥️ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 3 | x86_64-dragonfly |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 | x86_64-haiku |
🖥️⚡ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 | x86_64-illumos |
🖥️🛠️ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
| 3 | x86_64-serenity |
🖥️⚡ | ✅ | ✅ | ✅ | ✅ | ❔ | ❌️ | ❌️ |
|
|
|||||||||
| 4 | alpha-linux |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 4 | alpha-netbsd |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 4 | alpha-openbsd |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 4 | arc[eb]-linux |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ✅ | ❌️ |
| 4 | csky-linux |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ✅ | ❌️ |
| 4 | hppa-linux |
📄 | ❌️ | ❔ | ❌️ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | hppa-netbsd |
📄 | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | hppa-openbsd |
📄 | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | hppa64-linux |
📄 | ❌️ | ❔ | ❌️ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | m68k-linux |
🖥️ | ❌️ | ❔ | ✅ | ✅ | ❌ | ✅ | ❌️ |
| 4 | m68k-netbsd |
🖥️ | ❌️ | ❔ | ✅ | ✅ | ❌ | ✅ | ❌️ |
| 4 | m88k-openbsd |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 4 | microblaze[el]-linux |
📄 | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | or1k-linux |
📄 | ❌️ | ❔ | ✅ | ✅ | ❌ | ❌️ | ❌️ |
| 4 | sh[eb]-linux |
📄 | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | sh[eb]-netbsd |
📄 | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | sh-openbsd |
📄 | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
| 4 | sparc-linux |
🖥️ | ❌️ | ❔ | ✅ | ✅ | ❌ | ✅ | ❌️ |
| 4 | sparc-netbsd |
🖥️ | ❌️ | ❔ | ✅ | ❌️ | ❌ | ✅ | ❌️ |
| 4 | sparc64-netbsd |
🖥️🛠️ | ⚠️ | ✅ | ✅ | ❌️ | ✅ | ✅ | ❌️ |
| 4 | sparc64-openbsd |
🖥️🛠️ | ⚠️ | ✅ | ✅ | ❌️ | ❌ | ✅ | ❌️ |
| 4 | xtensa[eb]-linux |
🖥️ | ❌️ | ❔ | ✅ | ❌️ | ❌ | ❌️ | ❌️ |
The Zig standard library has minimum version requirements for some supported operating systems, which in turn affect the Zig compiler itself:
| OS | Version |
|---|---|
| Darwin | 15.0+ |
| DragonFly BSD | 6.4+ |
| FreeBSD | 14.0+ |
| Linux | 5.10+ |
| NetBSD | 10.1+ |
| OpenBSD | 7.8+ |
| Windows | 10+ |
Zig also has varying levels of support for these targets, for which the tier system does not quite apply:
aarch64-driverkit
aarch64[_be]-freestanding
aarch64-fuchsia
aarch64-hurd
aarch64-switch
aarch64-uefi
alpha-freestanding
amdgcn-amdhsa
amdgcn-amdpal
amdgcn-mesa3d
arc[eb]-freestanding
arm[eb]-freestanding
arm-3ds
arm-fuchsia
arm-gba
arm-uefi
arm-vita
avr-freestanding
bpf(eb,el)-freestanding
csky-freestanding
ez80-freestanding
ez80-tios
hexagon-freestanding
hppa[64]-freestanding
kalimba-freestanding
kvx-freestanding
lanai-freestanding
loongarch(32,64)-freestanding
loongarch(32,64)-uefi
m68k-freestanding
m88k-freestanding
microblaze[el]-freestanding
mips[64][el]-freestanding
mipsel-psx
mipsel-psp
msp430-freestanding
nvptx[64]-cuda
nvptx[64]-nvcl
or1k-freestanding
powerpc-wiiu
powerpc[64][le]-freestanding
powerpc64-ps3
propeller-freestanding
riscv(32,64)[be]-freestanding
riscv(32,64)-uefi
riscv64-fuchsia
riscv64-hurd
s390x-freestanding
sh[eb]-freestanding
sparc[64]-freestanding
spirv(32,64)-opencl
spirv(32,64)-opengl
spirv(32,64)-vulkan
spork8-freestanding
thumb[eb]-freestanding
thumb-fuchsia
thumb-gba
thumb-vita
ve-freestanding
wasm(32,64)-emscripten
wasm(32,64)-freestanding
x86[_16,_64]-freestanding
x86[_64]-hurd
x86[_64]-uefi
x86_64-driverkit
x86_64-fuchsia
x86_64-plan9
x86_64-ps4
x86_64-ps5
xcore-freestanding
xtensa[eb]-freestanding
Since the release of Zig 0.16.0, a lot of progress has been made towards stabilizing the language. This is a key step in our roadmap, and a requirement before tagging Zig 1.0.
In particular, since the last release, we have discussed and made decisions on many language proposals—accepting around 25 and rejecting around 125. At the time of writing, 23 undecided language proposals remain open on the Codeberg issue tracker, and 61 undecided language proposals remain open on the legacy GitHub issue tracker. Therefore, this effort represents a significant step towards finalizing the language design (although some major decisions remain).
@bitCast
changes §Zig 0.17.0 changes the definition of the @bitCast
builtin.
In many cases, the new behavior is equivalent to the old: in particular, casting between an integer type and another integer type is unaffected, as is casting between an integer type and a packed struct
or packed union
.
However, the semantics of @bitCast
calls involving array or vector types have changed. Unfortunately, this change has the potential to break existing code without triggering a compile error.. Therefore, it may be useful when upgrading to audit any @bitCast
uses which involve array or vector types.
The new definition of @bitCast
is that it reinterprets the logical bit representation of a value as a different type. The following types are considered to have logical bit representations:
void
bool
comptime_int
comptime_float
enum(T)
, packed struct(T)
, and packed union(T)
For integer and floating-point types, the logical bit representation starts with the least-significant bit and ends with the most-significant bit. For array and vector types, all elements' logical bit representations are concatenated in order starting with the first element.
In practice, this means that the new @bitCast
definition largely aligns with the old behavior on little-endian targets. Unlike the old behavior, the new behavior is fully endian-agnostic, i.e. the operation behaves the same regardless of the target endian.
The new @bitCast
definition disallows casts between some types which were previously allowed. In particular, casts involving extern struct
or extern union
types are no longer permitted. In most cases, code which was using such casts is aiming to reinterpret the value's in-memory representation (sometimes called "type punning")—to achieve this, use @ptrCast
or an extern union
.
⬇️
Zig's formal grammar.peg and the actual language implementation did not agree in many places. Probably, the formal grammar has never actually 100% matched the actual handwritten tokenizer and parser.
This problem is now fixed and unblocks future grammar changes and language specification work.
At a high level, the approach was to write a tool that accepts Zig's grammar.peg as input and outputs a
simple recursive descent parser. This generated parser is then used as an oracle for fuzz testing and the
handwritten std.zig.Ast.parse()
is compared against it. This approach ensures a
single source of truth and allows for easy iteration as the grammar changes.
More details: #36094
@cImport
was deprecated in Zig 0.16.0 and is now removed. Furthermore, in this release
std.Build.Step.TranslateC
is deprecated in favor of an explicit package dependency on
official ZSF translate-c package, which is the same
implementation the build step provides, but offers
more configuration options for the
translated code, and has an independent release cadence from the main Zig toolchain.
Upgrade guide:
zig fetch --save git+https://codeberg.org/ziglang/translate-c
--- a/build.zig
+++ b/build.zig
@@ -1,16 +1,19 @@
const std = @import("std");
+const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
- const translate_c = b.addTranslateC(.{
- .root_source_file = b.path("src/c.h"),
+ const translate_c = b.dependency("translate_c", .{});
+
+ const translator: Translator = .init(translate_c, .{
+ .c_source_file = b.path("src/c.h"),
.target = target,
.optimize = optimize,
+ // additional options now available that go here:
+ // https://codeberg.org/ziglang/translate-c#options
});
- translate_c.linkSystemLibrary("glfw", .{});
- translate_c.linkSystemLibrary("epoxy", .{});
+ translator.linkSystemLibrary("glfw3", .{});
+ translator.linkSystemLibrary("epoxy", .{});
const exe = b.addExecutable(.{
.name = "tetris",
@@ -21,7 +24,7 @@ pub fn build(b: *std.Build) void {
.imports = &.{
.{
.name = "c",
- .module = translate_c.createModule(),
+ .module = translator.mod,
},
},
}),
@backingInt
and @fromBackingInt
§The @backingInt
and @fromBackingInt
builtins are new.
These builtins replace the now-deprecated @intFromEnum
and
@enumFromInt
builtins (#35966).
@backingInt
works with all enums and with bitpacks with explicit backing
integer types only. It also works with tagged unions, returning the backing
integer of the active tag value.
An undefined
enum or bitpack yields an undefined
backing integer.
@fromBackingInt
infers its result type, which may be any enum or a bitpack with an
explicit backing integer type. It takes a parameter of exactly that backing integer type. For enums,
passing a backing integer that is either undefined
or would yield an invalid tag
value results in safety-checked Illegal Behavior. For bitpacks, passing an undefined
backing integer yields an undefined
bitpack.
@bitCast
now also performs a safety check for invalid tag values if its
destination type is an enum
.
Also adds a std.meta.BackingInt
function to get the result
type of @backingInt
.
Zig now requires empty enums to have noreturn
as their backing integer because
they are uninstantiable.
Upgrade example:
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -145,7 +145,7 @@ pub const Graph = struct {
pub fn addGeneratedFile(graph: *Graph, owner: *Step) Configuration.GeneratedFileIndex {
graph.generated_files.append(graph.arena, owner) catch @panic("OOM");
- return @enumFromInt(graph.generated_files.items.len - 1);
+ return @fromBackingInt(@intCast(graph.generated_files.items.len - 1));
}
pub fn dupeString(graph: *const Graph, bytes: []const u8) []const u8 {
@@ -2607,7 +2607,7 @@ pub const LazyPath = union(enum) {
.src_path, .cwd_relative, .relative, .dependency => {},
.generated => |gen| {
const graph = other_step.owner.graph;
- const generated_owner_step = graph.generated_files.items[@intFromEnum(gen.index)];
+ const generated_owner_step = graph.generated_files.items[@backingInt(gen.index)];
other_step.dependOn(generated_owner_step);
},
}
zig fmt automatically performs this upgrade.
@SpirvType
§SPIR-V has a number of types, such as images and samplers, that have no equivalent in Zig's type
system. Previously, the only way to refer to one of them was through inline assembly, which made it
impossible to declare a texture or a storage buffer as an ordinary global variable. Zig 0.17.0
implements accepted proposal #35240,
adding the @SpirvType
builtin alongside the other type-creating builtins:
.sampler
creates an OpTypeSampler
..image
creates an OpTypeImage
..sampled_image
creates an OpTypeSampledImage
from an image
type whose usage is .sampled
..runtime_array
creates an OpTypeRuntimeArray
.
It supports indexing and exposes a len
field just like the array type.Using this builtin when not targeting SPIR-V is a compile error. The options are also validated against the target OS.
Array multiplication syntax (a ** b
) has been removed in favor of
@splat
.
Migration:
--- a/player/chromaprint.zig
+++ b/player/chromaprint.zig
@@ -35,7 +35,7 @@ pub const Chroma = struct {
const max_index = @min(window_size / 2, freqToIndex(max_freq));
const notes: [window_size]u8 = n: {
@setEvalBranchQuota(window_size);
- var result = [1]u8{0} ** window_size;
+ var result: [window_size]u8 = @splat(0);
for (min_index..max_index) |i| {
const freq = indexToFreq(i);
const octave = freqToOctave(freq);
@@ -123,7 +123,7 @@ const RollingIntegralImage = struct {
num_rows: u32,
pub const init: RollingIntegralImage = .{
- .data = [1]Float{0} ** data_size,
+ .data = @splat(0),
.num_rows = 0,
};
@divCeil
§The new @divCeil
builtin performs integer division rounded toward positive
infinity, complementing the existing @divTrunc
, @divFloor
,
and @divExact
builtins.
As with the other division builtins, caller guarantees that denominator != 0
and
that result does not overflow.
No more std.math.divCeil(a, b) catch unreachable
!
@hasDecl
Returns true
Only for Public Declarations §Previously, @hasDecl
returned true
for public declarations
and declarations in the same file. Now, the behavior is the same independently of which file
@hasDecl
is in.
comptime
Length Slices §Now allowed:
void{}
Syntax Removed §void{}
is no longer valid syntax. Use {}
instead (#15213).
errdefer
Capture Removed §The capture (|err|
) is no longer allowed (#23734).
To migrate, split the function into two:
fn processOneTarget(job: Job) void {
- errdefer |err| std.debug.panic("panic: {s}", .{@errorName(err)});
+ processOneTargetInner(job) catch |err| std.debug.panic("panic: {s}", .{@errorName(err)});
+}
+fn processOneTargetInner(job: Job) !void {
const target = job.target;
i0
Removed §i0
is no longer an allowed primitive integer type.
This type was nonsensical, so does not have a direct alternative. However, any uses of it can almost certainly be transparently replaced with u0
.
internal
and link_once
Global Linkage Removed §The internal
and link_once
tags of
std.lang.GlobalLinkage
have been removed as they had unclear semantics and
incomplete support in codegen and linking (#36956). Any use of link_once
is likely served by
weak
, while the replacement for internal
is to simply not
@export
the symbol in the first place.
f128
support for @exp
and
@exp2
based on "Table-Driven Implementation of the Exponential Function in IEEE
Floating-Point Arithmetic" by Ping Tak Peter Tang, adapted to work with 128-bit numbers (#31846).std.Io.Semaphore.waitTimeout
(#31924).std.spirv
helpers for sampling, querying, and writing images
(#36187).ArrayHashMap.setKey
no longer recomputes the entire index (#32136).std.Target.parseCpuModel
now returns optional rather than error.std.debug.Pdb
: deduplicate inline source locations (#35438).hash.crc
full namespace audit (#35952).std.fs.path
add appending variants for relative
and
resolve
(#36784).std.heap.memory_pool.AlignedManaged
removed in favor of
std.heap.memory_pool.Aligned
.std.heap.memory_pool.ExtraManaged
removed in favor of
std.heap.memory_pool.Extra
.std.builtin
in favor of std.lang
.std.meta.fieldInfo
in favor of @typeInfo
.std.meta.fieldNames
in favor of @typeInfo
.std.meta.fieldTypes
in favor of @typeInfo
.std.DoublyLinkedList.pop
in favor of
std.DoublyLinkedList.popLast
.std.gpu
to std.spirv
.std.ascii.indexOfIgnoreCase
in favor of std.ascii.findIgnoreCase
.std.ascii.indexOfIgnoreCasePos
in favor of std.ascii.findIgnoreCasePos
.std.ascii.indexOfIgnoreCasePosLinear
in favor of std.ascii.findIgnoreCasePosLinear
.std.bit_set.Integer.initEmpty
in favor of std.bit_set.Integer.empty
.std.bit_set.Integer.initFull
in favor of std.bit_set.Integer.full
.std.bit_set.Array.initEmpty
in favor of std.bit_set.Array.empty
.std.bit_set.Array.initFull
in favor of std.bit_set.Array.full
.std.enums.EnumSet.initEmpty
in favor of std.enums.EnumSet.empty
.std.enums.EnumSet.initFull
in favor of std.enums.EnumSet.full
.std.mem.containsAtLeastScalar2
in favor of std.mem.containsAtLeastScalar
.std.mem.readPackedIntNative
in favor of std.mem.readPackedInt
.std.mem.readPackedIntForeign
in favor of std.mem.readPackedInt
.std.mem.writePackedIntNative
in favor of std.mem.writePackedInt
.std.mem.writePackedIntForeign
in favor of std.mem.writePackedInt
.StackFallbackAllocator
is an abstraction that is useful for the "small
vec" optimization, in which common cases can fit on a pre-allocated stack buffer, but rare cases
need dynamic heap allocation. The previous design had a few problems:
.allocator().get()
mutated the type, unlike all other allocators,
requiring a runtime safety check.Now, the buffer is provided as an argument, like most other std APIs that need a buffer.
Migration guide:
⬇️
std.heap.DebugAllocator
is replaced by a thread-safe allocator with the following
guarantees:
deinit
reports all leaks and frees all backing memory.SafeAllocator
instances cause a panic (if
Options.canary
differ).Given the backing allocator does not reuse memory, it does not reuse memory either and most writes after free will segmentation fault or are eventually detected and panic.
std.heap.DebugAllocator
and std.heap.Check
are
deprecated.
Every allocation is trailed by an AllocFooter
which contains metadata for the
allocation and stack traces. It is protected by a checksum to catch corruption from allocation overwrites
and report canary mismatches. An allocation's memory has a minimum alignment of
AllocFooter
so that the footer is at a fixed offset determined from the allocation
size. An allocation's memory is stored either:
To track allocations, each thread maintains a table of backing allocations. The table may be modified by other threads in the case of a producer-consumer operation, so the table is a linked list only expanded by creating new segments. Each thread maintains a linked list of free entries, which may contain entries from other threads' tables.
In the case of producer-consumer operations, acquire/release ordering is assumed to be provided externally. This is also assumed by all other thread-safe allocators that reuse memory as otherwise there would be data races on reuse of allocated memory.
Two fuzz tests have also been added for the allocator. They check that there is no memory reuse, that returned memory is writable, and that it is not overwritten. The multi-threaded fuzz test spawns a number of worker threads which are used for all the test runs. I have run these tests extensively under TSAN.
Building the standard library tests with an -Osafe
compiler build and
-Ddebug-allocator
:
Benchmark 1 (3 runs): ./master-out/bin/zig test --zig-lib-dir lib lib/std/std.zig -femit-bin=test --test-no-exec
measurement mean ± σ min … max outliers delta
wall_time 29.4s ± 157ms 29.2s … 29.5s 0 ( 0%) 0%
peak_rss 2.24GB ± 3.49MB 2.23GB … 2.24GB 0 ( 0%) 0%
cpu_cycles 143G ± 999M 142G … 144G 0 ( 0%) 0%
instructions 268G ± 5.22M 268G … 268G 0 ( 0%) 0%
cache_references 13.1G ± 88.8M 13.0G … 13.2G 0 ( 0%) 0%
cache_misses 2.38G ± 30.7M 2.35G … 2.41G 0 ( 0%) 0%
branch_misses 634M ± 6.22M 629M … 641M 0 ( 0%) 0%
Benchmark 2 (3 runs): ./branch-out/bin/zig test --zig-lib-dir lib lib/std/std.zig -femit-bin=test --test-no-exec
measurement mean ± σ min … max outliers delta
wall_time 22.1s ± 88.6ms 22.0s … 22.2s 0 ( 0%) ⚡- 24.7% ± 1.0%
peak_rss 1.11GB ± 799KB 1.11GB … 1.11GB 0 ( 0%) ⚡- 50.3% ± 0.3%
cpu_cycles 136G ± 480M 136G … 137G 0 ( 0%) ⚡- 4.4% ± 1.2%
instructions 273G ± 2.07M 273G … 273G 0 ( 0%) 💩+ 1.6% ± 0.0%
cache_references 12.3G ± 71.3M 12.2G … 12.4G 0 ( 0%) ⚡- 6.0% ± 1.4%
cache_misses 2.02G ± 11.5M 2.01G … 2.03G 0 ( 0%) ⚡- 14.9% ± 2.2%
branch_misses 569M ± 2.65M 567M … 572M 0 ( 0%) ⚡- 10.2% ± 1.7%
getLastOrNull
has been deprecated and renamed to last
getLast
has been deprecated in favor of last
combined
with .?
lastPtr
has been added which returns ?*T
Upgrade guide:
⬇️
This is an enhancement that can help track down ArrayList usage bugs faster (#36239).
debug.SafetyLock
Gains Support for Shared Locking §The existing lock
and unlock
methods continue to act
exclusively. They should be used when data may be mutated. New methods, lockShared
and unlockShared
, may be used for shared locking in situations where multiple
independent users are reading but not mutating data.
fmt.allocPrint
moved to mem.Allocator
§⬇️
The "{q}"
specifier which escapes strings so that they can appear in
double-quoted string literals has relaxed escaping rules such that UTF-8 encoded data can pass through
unmangled.
"{qf}"
is introduced for double-quote escaping the output of
a format()
.
std.zon.parse
Reworked §std.zon.parse
now takes struct args and allocates its result from an arena.
Migration guide:
⬇️
Some methods were renamed:
fromSliceAlloc
➡️ fromSlice
fromSlice
➡️ fromSliceNoAlloc
"updateFrom" variants such as updateFromSlice
were added. These update an in
memory value, overwriting the value's fields with fields specified in the ZON source. This can be useful
when using ZON to load configuration files with varying precedence, for example a text editor that has a
global config file and a per-project config file.
bit_set
Variants and Deprecate the Managed One §Renames the types for consistency, deprecating the previous names and the managed variant.
std.bit_set.IntegerBitSet
➡️ std.bit_set.Integer
std.bit_set.ArrayBitSet
➡️ std.bit_set.Array
std.StaticBitset
, std.bit_set.StaticBitSet
➡️
std.bit_set.Static
std.DynamicBitSetUnmanaged
,
std.bit_set.DynamicBitSetUnmanaged
➡️ std.bit_set.Dynamic
std.DynamicBitSet
, std.bit_set.DynamicBitSet
➡️
std.bit_set.DynamicManaged
(deprecated)std.lang.Type
§When doing type reflection, structs and unions return their information in struct-of-arrays style (#35234).
--- a/lib/compiler/Maker/ScannedConfig.zig
+++ b/lib/compiler/Maker/ScannedConfig.zig
@@ -49,9 +49,10 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
}
fn printStruct(sc: *const ScannedConfig, s: *Serializer.Struct, comptime S: type, v: S) !void {
- inline for (@typeInfo(S).@"struct".fields) |field| {
- try s.fieldPrefix(field.name);
- try printValue(sc, s.container.serializer, field.type, @field(v, field.name));
+ const info = @typeInfo(S).@"struct";
+ inline for (info.field_names, info.field_types) |field_name, field_type| {
+ try s.fieldPrefix(field_name);
+ try printValue(sc, s.container.serializer, field_type, @field(v, field_name));
}
}
lang.OptimizeMode
to lang.Optimize
§And remove "release" from the enum tag names.
No functional change, however, despite the addition of backwards-compatibile declarations in this patch,
it is breaking because expressions that use ==
or !=
operators will not able to use the deprecated names.
std.lang
: OptimizeMode
➡️ Optimize
Debug
➡️- debug
ReleaseSafe
➡️- safe
ReleaseFast
➡️- fast
ReleaseSmall
➡️- small
lang.Optimize.runtimeSafety
§std.lang.Optimize.runtimeSafety
is preferred as an alternative to
std.debug.runtime_safety
since it will offer callsites knowledge about their own
module rather than standard library module.
The redundant constants cpu
, os
,
abi
, and object_format
in
@import("builtin")
have been
deprecated and will be removed in 0.18.0. Please replace any usage with the corresponding fields on the
target
constant:
@import("builtin").cpu
➡️ @import("builtin").target.cpu
@import("builtin").os
➡️ @import("builtin").target.os
@import("builtin").abi
➡️ @import("builtin").target.abi
@import("builtin").object_format
➡️ @import("builtin").target.ofmt
mem.eql
and mem.findDiff
§The functions std.mem.eql
and std.mem.findDiff
short-circuit when their two inputs are slices to the same memory. This short-circuiting is only correct
when the ==
operator, for the given type, is reflexive. This isn't the case for
floats, as for example std.math.nan(f64) != std.math.nan(f64)
. The change in this PR
disables that optimisation when working on float slices.
Previously-failing, now-succeeding tests:
Uri
and net.HostName
§Uri was sometimes using HostName.validate for host
(in resolveInPlace) and sometimes not (in
parseAfterScheme). On its own, this was a problem, but the bigger problem is that RFC3986 (Uri) has a much
different idea of what a valid host name is than RFC1123 (HostName), and so just making Uri consistently
use HostName.validate would make Uri less useful overall.
Instead, all HostName
-related stuff has been removed from
Uri
. Uri.getHost
has been moved to
HostName.fromUri
(without a graceful deprecation, since the semantics are different enough for users to
need to evaluate usage sites), while Uri.getHostAlloc
has been removed entirely.
Migration guide:
⬇️
b.build_root
(Directory) ➡️ b.root
(Path)ConfigHeader.Options
: include_guard_override
➡️
include_guard
LazyPath
: getDisplayName
➡️ format
("{f}"
)LazyPath.basename
: removed since the value is not known until make phaseb.findProgram
divided into findProgram
and
findProgramLazy
and API future-proofed.ConfigHeader
fixed; now reports unused values for all stylesaddArtifactArg
, addPrefixedArtifactArg
➡️ addArtifactArg2
addOutputFileArg
, addPrefixedOutputFileArg
➡️ addOutputFileArg2
addFileContentArg
, addPrefixedFileContentArg
➡️ addFileContentArg2
addOutputDirectoryArg
, addPrefixedOutputDirectoryArg
➡️ addOutputDirectoryArg2
addDirectoryArg
, addPrefixedDirectoryArg
, addDecoratedDirectoryArg
➡️ addDirectoryArg2
addDepFileOutputArg
, addPrefixedDepFileOutputArg
➡️ addDepFileOutputArg2
addFileArg
, addPrefixedFileArg
➡️ addFileArg2
zig build
now runs projects' build.zig
code in a separate executable than the
one that performs Package Management and executes the build graph, making
zig build
faster for several reasons
(#35428):
maker
executable remains unmodified when build.zig
script is edited,
and therefore only needs to be built exactly once ("first time setup") after installing Zig.maker
executable is built with optimizations enabled, which is starting to become
more valuable now that we have introduced --watch
and --fuzz
.build.zig
logic can be skipped sometimes depending on what CLI flags are used with
zig build
.Furthermore, configuration is now serialized into a compact binary format that can be consumed by third party tooling and is part of the new Build Server Protocol. The prior way of satisfying this use case by forking the build runner is no longer supported.
To render configuration as .zon to stdout, pass --print-configuration
.
New features:
All four combinations are possible (is_directory=true/false, metadata_mode=true/false). These features are exposed as new API in the Build System.
Since the Zig toolchain is heavily reliant on the caching system, this release also switches to a
binary format, saving roughly 25% on file size, which eases a bit of pressure on the file system cache
while also simplifying the work the computer needs to do - directly copy bytes from disk rather than
parsing text files. The new zig cache-cat
subcommand is available for troubleshooting or
tinkering with files inside a zig-cache directory.
The cache system also now has the capability to explain why a "miss" happened. The public-facing API of
std.Build.Cache
has many breaking changes, but outside of compiler tooling, this is
an uncommon API to be used, and all the changes make it harder to misuse.
This change has been observed to speed up cache hits by 5-10% (#36822).
If the cache is poisoned means that the configure logic had side effects, or otherwise did something that could not be tracked by the cache system.
This is not to be confused with whether individual steps may have side effects when being evaluated; it
has to do with the logic inside build.zig itself. For example, a Run
step that
prints "hello world" has side effects at make time and therefore does not warrant setting this flag,
while checking for the existence of scdoc
at configure time in order to choose the default
value for a configuration option does.
Keeping the cache pure will make zig build
faster, bypassing the configurer process when
identical configuration would be generated.
When the cache is poisoned, the maker process will delete the build configuration file upon ingesting it since it cannot be reused.
Ways to poison the cache include calling findProgram, or more directly
std.Build.Graph.poisonCache
. A better alternative than cache poisoning is to
explicitly declare the configuration dependencies with these new functions:
std.Build.dependOnFileContents
- indicates that the build.zig logic depends on a particular file's contents.std.Build.dependOnFileMetadata
- indicates that the build.zig logic depends on a particular file's size, inode, mtime, and contents.std.Build.dependOnDirectoryContents
- indicates that the build.zig logic depends on a particular directory's entries.std.Build.dependOnDirectoryMetadata
- indicates that the build.zig logic
depends on a particular directory's last modification date.Advanced users can override the cache poisoning behavior with a new CLI option:
--cache-poison[=mode] Override configuration caching behavior
pure (default) Avoid false positive cache hits
poisoned Don't cache the configuration
disallowed Panics when cache would be poisoned
ignored A little poison never hurt anybody
Immediately (in the configure phase), searches for an executable on the host that has more than one possible name.
Names are searched in order, observing search prefixes first and then PATH environment variable.
Calling this function poisons the configuration cache, so it is only appropriate when the existence of the program or its output needs to be observed by configuration logic. That's why there is also findProgramLazy now.
Creates an anonymous Step
that searches for an executable on the host that
has more than one possible name.
Unlike findProgram, this function does not
poison the configuration cache, however
the result cannot be used in the configuration phase, hence the return type being
LazyPath
.
Returns the LazyPath
of the found executable. The search only takes place
if the LazyPath
will be used by a depending Step
.
This API is useful in the following cases:
In the Run step, passthru args are all together now, not observable in configure phase whether run args are provided.
--- build.zig
+++ build.zig
@
-if (b.args) |args| {
- run_cmd.addArgs(args);
-}
+run_cmd.addPassthruArgs();
This removes a capability from build scripts since they can no longer observe those arguments. In exchange, it means that when changing those arguments, build scripts no longer must be rebuilt from source.
paths
and exclude_paths
are now
LazyPath
lists. There is a convenience method to create them:
b.pathList
.
--- build.zig
+++ build.zig
@
- const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" };
- const fmt_exclude_paths = &.{ "test/cases", "test/behavior/zon" };
+ const fmt_include_paths = b.pathList(&.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" });
+ const fmt_exclude_paths = b.pathList(&.{ "test/cases", "test/behavior/zon" });
Step.Options
: add addOptionPathDirectory §Now, when adding an option that is a file path, one must explicitly choose between (#36876):
addOptionPath
(must be a file)addOptionPathDirectory
(must be a directory)addOptionPathUntracked
(opt out of dependency tracking)std.Build.dependency
: support lazy dependenciesstd.Build.dependencyLazy
which possibly returns
error.LazyDependencyNeeded
instead of null
, so that you can
use try
build
functions return
error.LazyDependencyNeeded
, build system proceeds to fetch them rather than failing
configuration.There is no concept of a "build runner" any more; it has been split into: configurer and maker
This use case is now handled by the Build Server Protocol.
All package management functionality has been moved out of the Compiler and into the Build System. This includes the following sub-commands:
zig build
zig fetch
zig init
zig libc
zig cache-cat
This means that large parts of what used to be included in the compiler executable are now shipped in source form instead, including:
All of this functionality is now compiled in -Osafe
optimization mode rather than -Ofast
due to being in the compiler. When hacking on
the build system itself, the environment variable ZIG_DEBUG_CMD=1
may be used to
compile the build system in debug mode instead.
Miscellaneous changes:
--pkg-path
CLI arg and ZIG_LOCAL_PKG_DIR
env var are now observed for both
fetch and build commands.
Now zig fetch
only fetches into the global cache, just like it used to. However, if
--save
(or any variant) is used, then it also fetches into the local package path. When
fetching globally, does not require build.zig
to be present. zig build
always
fetches locally (in addition to globally).
Notably, this fixes the regressed use case zig fetch .
When fetching by path, the hash is always computed, recompressed tarball is always created, always overwrites any existing global cache entry.
It used to be the case that, when targeting Windows or Wine, artifact args added to Run steps modified PATH based on the set of directories containing the recursive set of DLL dependencies. Now this is only done for argv[0]. The motivation for also doing this for the other command line arguments is unclear, since those DLLs don't need to be loaded in order to execute argv[0].
Now, when --listen=-
is passed, the build system serves a protocol that allows connected
clients to monitor and control the build graph as it executes. This is intended to be consumed by
third-party tooling such as IDEs.
Current things you can do:
In particular, the separation of maker process and configurer process is a breaking change that prevents the ZLS project from working with 0.17.0. Although some progress was made to restore functionality in this release cycle, Zig team and ZLS team are still working together to enhance the build server protocol further to the point that ZLS can not only restore functionality, but surpass the power and capabilities compared to before.
In the future it is expected for much of Zig's own first-party build system tooling to become a client of the build server protocol, dogfooding it to ensure that third-party tooling enjoys equivalent capabilities (#36497).
It is also planned for the build server to multiplex compiler server protocol for the compilation steps, providing type-system information, refactoring, and other advanced editing capabilities (#615).
The Zig compiler's implementation of incremental compilation—a feature allowing near-instant rebuilds of projects after changing the code—has been significantly improved in Zig 0.17.0. Many bugs have been fixed, and the new ELF Linker introduced in the previous release has gained good support for the feature.
Thanks to these enhancements, it is now possible for most projects targeting
x86_64-linux
to take advantage of incremental compilation. To do so, add
the arguments -fincremental --watch
to your zig build
command (e.g.
zig build -fincremental --watch
)—this will cause the Zig build system to
listen for changes to source files, and react to them by performing an incremental rebuild.
For more information on ways to use incremental compilation in your own projects, or to learn more about how this feature works under the hood, consider checking out this blog post by a Zig core team member.
Future releases will continue to focus on improving this feature, including introducing a new
Mach-O linker and self-hosted aarch64 Backend with good support for incremental
compilation; adding support for using incremental compilation without --watch
; and
fixing any remaining bugs.
The self-hosted SPIR-V backend is now multi-threaded like the other backends.
Execution modes such as LocalSize
and OriginUpperLeft
are now derived
from the function's calling convention instead of being set through inline assembly, and the new
spirv_task
and spirv_mesh
calling conventions add
support for task and mesh shaders (#35676).
Declaring capabilities and extensions in inline assembly with OpCapability
and
OpExtension
is no longer allowed. They are enabled through target CPU features
instead, i.e. the -mcpu
option.
22 bugs were fixed in the SPIR-V backend during this release cycle.
Progress towards this is blocked on Linker enhancements, many of which were completed during this release cycle.
Initial implementation of self-hosted backend for loongarch64 has been contributed (#36418). It is still experimental and not yet usable. There are two ways to contribute to this backend: working on it directly, and contributing to AIR Legalization Features, which helps all unfinished backends reach the finish line quicker.
Zig's WebAssembly backend is now passing 2060/2054 (100%) behavior tests compared to the LLVM backend. However, it is not yet the default when compiling in debug optimization mode due to lack of debug info support (#37032).
This release makes significant progress towards replacing Zig's legacy self-hosted ELF linker with its new implementation introduced in the previous release. Specific enhancements include:
While this linker has not quite reached feature parity with our old self-hosted ELF linker
yet—and so remains disabled by default—it is already capable in practice of building
the vast majority of Zig projects targeting x86_64-linux
. This unlocks the ability
to use Incremental Compilation for these projects—like in Zig 0.16.0, the new
linker is enabled by default in this case.
In the next release of Zig, we hope to fully eliminate the legacy ELF linker in favour of this implementation.
COFF support in the linker is enhanced with the following features (#35674):
__dllimport
support: Indirect calls / loads from the IAT directly-gnu
: Constructor / destructor support (ie. merge .ctor and .dtor, and set up the
__CTOR_LIST__
, __DTOR_LIST__
symbols).drectve
arguments (these are required to correctly link
msvc
libc):/INCLUDE
: Forcing a symbol to be referenced/ALTERNATENAME
: Adding symbol aliases/MERGE
: Section merging. This functionality is also used to direct certain sections into the right
place (like .ctor
/ .dtor
into .rdata
)/DEFAULTLIB
: Adding new inputsZig is moving towards snapshot-based testing for its linkers.
Tests are a combination of comparing objdump snapshot output, actually running the artifacts, and checking for linker errors.
zig build -Dlink-snapshot-update
causes tests to run in a mode that outputs snapshots
instead of checking against them.
A typical workflow for adding a new test:
zig-debug build test-link -Dtest-filter=my-test -Dlink-snapshot-update
verifyObjdump
calls.zig-debug build test-link -Dtest-filter=my-test
scope
parameter should be used to cause -Dlink-snapshot-update
to output snapshot
to different filenames, scoped on the diference.-Dlink-snapshot-update
to update the new set of snapshots.The SPIR-V linker has been rewritten (#36828).
It now supports incremental compilation and can link external .spv
object files.
Although this release's Build System changes are loosely related to Zig's integrated fuzzer and its interaction with the build system, no changes were made to the fuzzer itself.
We expect to focus on improving the fuzzer in a future release cycle.
Full list of the 329 bug reports closed during this release cycle:
Many bugs were both introduced and resolved within this release cycle. Most bug fixes are omitted from these release notes for the sake of brevity.
Zig has known bugs, miscompilations, and regressions.
Even with Zig 0.17.x, working on a non-trivial project using Zig may require participating in the development process.
When Zig reaches 1.0.0, Tier 1 support will gain a bug policy as an additional requirement.
We are aware of these notable regressions in 0.17.0:
std.debug.simple_panic
fails to compilestd.Build.Step.Run
This release of Zig upgrades to LLVM 22.1.8. This covers Clang (zig cc), libc++, libc++abi, libunwind, and libtsan as well.
In the previous release of Zig, we were forced to disable a key LLVM optimization pass—loop vectorization—to work around a miscompilation which affected the Zig compiler.
Since we first introduced that workaround, a fix has been merged into LLVM's main branch. However, the fix is not available in LLVM 22, the LLVM version used by Zig 0.17.0. Therefore, this workaround remains enabled for now.
Zig 0.18.0 will upgrade to LLVM 23, so will allow us to re-enable this optimization pass.
Zig 0.17.0 distributes musl 1.2.5 plus backported security and portability fixes. Meanwhile, upstream has tagged 1.2.6. Zig 0.18.0 will update to musl 1.2.6.
When targeting musl statically, many functions are now provided by zig libc rather than source files copied from musl. Therefore, if you encounter bugs with musl libc provided by Zig, please respect upstream by reporting them to Zig's issue tracker rather than musl's.
glibc version 2.44 is now available when cross-compiling.
This release includes Linux kernel headers for version 7.2.
This release includes macOS system headers for version 27.0.
Zig 0.17.distributes MinGW-w64 commit 31bd54ab7d5fe03c67ed2bb1a57e531b9c7f8cc4
.
However, many functions are now provided by zig libc rather than source files copied from MinGW-w64. Therefore, if you encounter bugs with MinGW-w64 libc provided by Zig, please respect upstream by reporting them to Zig's issue tracker rather than MinGW-w64's.
NetBSD libc version 11.0 is now available when cross-compiling.
OpenBSD libc version 7.9 is now available when cross-compiling.
Zig 0.17.0 continues to distribute WASI libc
commit c89896107d7b57aef69dcadede47409ee4f702ee
.
However, many functions are now provided by zig libc rather than source files copied from WASI libc.
Furthermore, starting with Zig 0.18.0, instead of distributing third party WASI libc code, Zig will provide libc for WASI targets via zig libc. For more information, see:
In libc.txt
files, the gcc_dir
field has been renamed to cc_dir
to reflect the reality that it is not specific to GCC. The old name will still be accepted for now, but
users are encouraged to migrate their libc.txt
to the new name (#36951).
Additionally, cc_dir
is now required on Linux targets. Note that, because Android and OpenHarmony
store the relevant object files in an unusual location, users of these targets will likely want to set
cc_dir
to the same path as crt_dir
.
zig cc
and zig c++
are now based on Clang 22.1.8.
This was a requirement for snapshot testing, as well as aiding in developing the COFF Linker.
Supported features:
Usage: zig objdump [options] file
Options:
-h, --help Print this help and exit
--all-headers Alias for --file-headers --linker-member=2 --member-headers --section-headers --relocs --symbols
--exports[=sort] Display exported symbols.
In the case of COFF import libraries, displays the symbol list and import headers.
Specify =sort to optionally sort the import headers by symbol name.
--file-headers Display file-format specific headers
--imports Display imported symbols
--linker-member[=1|2|longnames] (Coff) Display contents of the specified archive linker member (default 2)
--member-headers Display archive member headers
--elements=[e1],[e2],-[e3],... Select which formatting elements are displayed. Intended for snapshot testing.
file-type File type summary
header-name Name that precedes a header block
member-path Display full member paths. If removed, only basenames will be used.
newlines Newlines between output sections
table-header Table headers with column names
all (default) All of the above
--only-member=[name] Only consider archive members names that contain [name]. Can be specified multiple times.
--only-section=[name] Only consider section names that contain [name]. Can be specified multiple times.
--only-symbol=[name] Only consider symbol names that contain [name]. Can be specified multiple times.
--redact=[kind] Redact the specified field kind. Intended for snapshot testing.
rva Relative virtual addresses
va Virtual addresses and file offsets
ord Symbol ordinals / hints
size Sizes and lengths
all All of the above
--relocs Display relocations
-s, --snapshot Alias for --redact=all --elements=-all
--section-headers Display section headers
--strings Display string tables
--symbols Display symbol tables
--tls Display TLS information
Some example output:
❯ zig objdump mathtest-dync-exe-no-llvm.dll --all-headers
mathtest-dync-exe-no-llvm.dll: PE/COFF image
COFF Header:
8664 machine (AMD64)
7 number_of_sections
6a08670a time_date_stamp
0 pointer_to_symbol_table
0 number_of_symbols
f0 size_of_optional_header
2022 flags
| EXECUTABLE_IMAGE
| LARGE_ADDRESS_AWARE
| DLL
COFF Optional Header:
20b magic (PE32+)
14.00 linker_version
14a400 size_of_code
84e00 size_of_initialized_data
0 size_of_uninitialized_data
1000 address_of_entry_point ( 180001000)
1000 base_of_code ( 180001000)
180000000 image_base
1000 section_alignment
200 file_alignment
6.00 operating_system_version
1.00 image_version
6.00 subsystem_version
0 win32_version_value
1d6000 size_of_image
400 size_of_headers
0 checksum
2 subsystem (WINDOWS_GUI)
160 dll_flags
| HIGH_ENTROPY_VA
| DYNAMIC_BASE
| NX_COMPAT
100000 size_of_stack_reserve
1000 size_of_stack_commit
100000 size_of_heap_reserve
1000 size_of_heap_commit
0 loader_flags
10 number_of_rva_and_sizes
Data Directories:
1b48d0 54 EXPORT
1b4924 8c IMPORT
0 0 RESOURCE
1cc000 633c EXCEPTION
0 0 SECURITY
1d4000 1280 BASERELOC
1bc000 1c DEBUG
0 0 ARCHITECTURE
0 0 GLOBALPTR
1aebc8 28 TLS
0 0 LOAD_CONFIG
0 0 BOUND_IMPORT
1b4c80 2d0 IAT
0 0 DELAY_IMPORT
0 0 COM_DESCRIPTOR
0 0 RESERVED
Sections in 'mathtest-dync-exe-no-llvm.dll':
Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags
1 .text 1000 14a346 14a400 400 0 0 0 0 60000020 | CNT_CODE MEM_EXECUTE MEM_READ
2 .rdata 14c000 6fe3c 70000 14a800 0 0 0 0 40000040 | CNT_INITIALIZED_DATA MEM_READ
3 .buildid 1bc000 52 200 1ba800 0 0 0 0 40000040 | CNT_INITIALIZED_DATA MEM_READ
4 .data 1bd000 e2a0 d200 1baa00 0 0 0 0 c0000040 | CNT_INITIALIZED_DATA MEM_READ MEM_WRITE
5 .pdata 1cc000 633c 6400 1c7c00 0 0 0 0 40000040 | CNT_INITIALIZED_DATA MEM_READ
6 .tls 1d3000 20 200 1ce000 0 0 0 0 c0000040 | CNT_INITIALIZED_DATA MEM_READ MEM_WRITE
7 .reloc 1d4000 1280 1400 1ce200 0 0 0 0 42000040 | CNT_INITIALIZED_DATA MEM_DISCARDABLE MEM_READ
No symbol table found
If this output was used for a snapshot test that wanted to test the presence of a particular export:
❯ zig objdump mathtest-dync-exe-no-llvm.dll --exports --only-symbol=add --redact=rva --elements=-all
Export directory:
0 flags
0 time_date_stamp
0.00 version
xxxxxxxxxxxxxxxx name_rva
1 ordinal_base
1 number_of_entries
1 number_of_names
xxxxxxxxxxxxxxxx export_address_table_rva
xxxxxxxxxxxxxxxx name_pointer_table_rva
xxxxxxxxxxxxxxxx ordinal_table_rva
1 0 xxxxxxxx | add
The redaction (--redact=
) and element removal (--elements=
) functionality is used to remove parts of
the output that don't matter for the particular test, as to not cause spurious failures if say, an RVA for
a symbol changes due to some unrelated change to the linker. -s
is shorthand which enables all the
redacts and removes all extra output elements, but particular tests may not use this if they do care about
specific values.
Windows resource script
compilation will be moved out of the compiler and into an official-but-external build system
package in the next release. As such, the corresponding std.Build
functions and fields
(Build.Module.addWin32ResourceFile
, etc) have been marked as deprecated in this release.
The zig rc
subcommand will remain after this change in order to continue supporting the use
case of using the Zig toolchain with other build systems.
--complexity
Flag §It's a simple tool for counting tokens and AST nodes, which can be used to share how an edit to a source file increases or reduces complexity with a heuristic that is more insightful than line count.
For example, running it on old ELF linker directory:
info: src/link/Elf/gc.zig: tokens=1538 nodes=770
info: src/link/Elf/Atom.zig: tokens=15576 nodes=7768
info: src/link/Elf/Merge.zig: tokens=2288 nodes=1092
info: src/link/Elf/Thunk.zig: tokens=1128 nodes=519
info: src/link/Elf/SharedObject.zig: tokens=4566 nodes=2236
info: src/link/Elf/eh_frame.zig: tokens=4840 nodes=2335
info: src/link/Elf/Symbol.zig: tokens=3770 nodes=1872
info: src/link/Elf/synthetic_sections.zig: tokens=13287 nodes=6375
info: src/link/Elf/Object.zig: tokens=13903 nodes=6842
info: src/link/Elf/LinkerDefined.zig: tokens=4140 nodes=2091
info: src/link/Elf/relocatable.zig: tokens=3598 nodes=1848
info: src/link/Elf/Archive.zig: tokens=2176 nodes=1028
info: src/link/Elf/ZigObject.zig: tokens=20783 nodes=10274
info: src/link/Elf/AtomList.zig: tokens=1819 nodes=933
info: src/link/Elf/relocation.zig: tokens=1289 nodes=551
info: src/link/Elf/file.zig: tokens=2330 nodes=1087
info: total: tokens=97031 nodes=47621
Another example is after editing Lld.zig to use
arena.print
rather than std.fmt.allocPrint
. The
line count is roughly the same but --complexity
tells a different story:
In the future this metric may be helpful for an automatic import organizer to determine whether to create an import alias or not.
Here are all the people who landed at least one contribution into this release:
Special thanks to those who sponsor Zig. Because of diverse, recurring donations, Zig is driven by the open source community, rather than the goal of making profit. In particular, those below sponsored Zig for an average of $50/month or more during this release cycle using our preferred donation platform:
Special thanks also to TigerBeetle, Synadia Communications, and ZML for substantial contributions.
Zig v0.17.0 Zig (ORG)
Zig Software Foundation (ORG)
Roadmap (PERSON)
GitHub Sponsors (ORG)
Every.org (ORG)
the Build System (ORG)
changes@backingInt (PERSON)
Removederrdefer Capture Removedi0 Removedinternal (PERSON)
OptimizeMode (PERSON)
lang (ORG)
Optimize lang (ORG)
Optimize.runtimeSafety (ORG)
mem.findDiff Uri (PERSON)
The Support Table and Additional Platforms (ORG)
Zig (ORG)