Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
Backed by the Zig Software Foundation, the project is financially sustainable. These core team members work on Zig full-time:
Please consider a recurring donation to the ZSF to help us pay more contributors!
This release features 6 months of work: changes from 177 different contributors, spread among 2023 commits.
One sentence summary: The Toolchain "just works" in more cases, many bugs were fixed, the Self-Hosted Compiler is 44% complete, the Support Table is expanded, there were a handful of Language Changes, the project started Performance Tracking, and the Standard Library, although unstable, became more useful.
To many, the reaction to this announcement will be something like, "Oh? I thought he was already on the core zig team." This goes to show how obvious this decision was.
Isaac is a level-headed, talented, careful, patient, and diligent programmer. You might be viewing these release notes through his dynamic tiling Wayland compositor, River.
He also works on TigerBeetle - the world's fastest financial accounting database.
In addition to lending his technical expertise to the Zig project, Isaac has proven to be a steadfast community leader, setting an example for how to treat others with kindness and respect.
Please give him a warm welcome to the core Zig team.
A green check mark (✅) indicates the target meets all the requirements for the support tier. The other icons indicate what is preventing the target from reaching the support tier. In other words, the icons are to-do items. If you find any wrong data here please submit a pull request!
zig cc
, zig c++
and related toolchain commands support
this target.freestanding | Linux 3.16+ | macOS 10.13+ | Windows 8.1+ | WASI | |
---|---|---|---|---|---|
x86_64 | ✅ | ✅ | ✅ | ✅ | N/A |
x86 | ✅ | #1929 🐛📦 | 💀 | #537 🐛📦 | N/A |
aarch64 | ✅ | #2443 🐛 | ✅ | 🐛📦🧪 | N/A |
arm | ✅ | #3174 🐛📦 | 💀 | 🐛📦🧪 | N/A |
mips | ✅ | #3345 🐛📦 | N/A | N/A | N/A |
riscv64 | ✅ | #4456 🐛📦 | N/A | N/A | N/A |
sparcv9 | ✅ | #4931 🐛📦🧪 | N/A | N/A | N/A |
wasm32 | ✅ | N/A | N/A | N/A | ✅ |
free standing | Linux 3.16+ | macOS 10.13+ | Windows 8.1+ | FreeBSD 12.0+ | NetBSD 8.0+ | DragonFlyBSD 5.8+ | UEFI | |
---|---|---|---|---|---|---|---|---|
x86_64 | Tier 1 | Tier 1 | Tier 1 | Tier 1 | ✅ | ✅ | ✅ | ✅ |
x86 | Tier 1 | ✅ | 💀 | ✅ | 🔍 | 🔍 | N/A | ✅ |
aarch64 | Tier 1 | ✅ | Tier 1 | 🔍 | 🔍 | 🔍 | N/A | 🔍 |
arm | Tier 1 | ✅ | 💀 | 🔍 | 🔍 | 🔍 | N/A | 🔍 |
mips64 | ✅ | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | N/A |
mips | Tier 1 | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | N/A |
powerpc64 | ✅ | 📖 | 💀 | N/A | 🔍 | 🔍 | N/A | N/A |
powerpc | ✅ | ✅ | 💀 | N/A | 🔍 | 🔍 | N/A | N/A |
riscv64 | Tier 1 | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | 🔍 |
sparcv9 | Tier 1 | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | N/A |
zig targets
is guaranteed to include this target.freestanding | Linux 3.16+ | Windows 8.1+ | FreeBSD 12.0+ | NetBSD 8.0+ | UEFI | |
---|---|---|---|---|---|---|
x86_64 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 |
x86 | Tier 1 | Tier 2 | Tier 2 | ✅ | ✅ | Tier 2 |
aarch64 | Tier 1 | Tier 2 | ✅ | ✅ | ✅ | ✅ |
arm | Tier 1 | Tier 2 | ✅ | ✅ | ✅ | ✅ |
mips64 | Tier 2 | Tier 2 | N/A | ✅ | ✅ | N/A |
mips | Tier 1 | Tier 2 | N/A | ✅ | ✅ | N/A |
riscv64 | Tier 1 | Tier 2 | N/A | ✅ | ✅ | ✅ |
powerpc32 | Tier 2 | Tier 2 | N/A | ✅ | ✅ | N/A |
powerpc64 | Tier 2 | ✅ | N/A | ✅ | ✅ | N/A |
bpf | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
hexagon | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
amdgcn | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
sparc | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
s390x | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
lanai | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
csky | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
freestanding | emscripten | |
---|---|---|
wasm32 | Tier 1 | ✅ |
zig targets
will display the target if it is available.-femit-asm
and cannot emit object files
(-fno-emit-bin
enabled by default and cannot be overridden).Tier 4 targets:
Luuk de Gram writes:
During this release we hit some major milestones. The
Self-Hosted Linker for wasm was rewritten, with knowledge gained from
building a
stand-alone wasm linker.
The linker is now capable of building a memory layout with a virtual stack,
as well as perform relocations. Those are some of the neccesary features required
to be able to implement zig test
. As of today, the self-hosted wasm backend is
now capable of passing 13% of all the behavioral tests. With all of those changes,
contributing to the wasm backend is made a lot easier. The linker is now capable
enough where only knowledge about wasm is required to contribute to the backend.
Those features were implemented in the following Pull Requests:
zig test
(#10240)The following 4 new linker flags were added for WebAssembly (#8633):
--import-memory
- import memory from the environment--initial-memory=[bytes]
- initial size of the linear memory--max-memory=[bytes]
- maximum size of the linear memory--global-base=[addr]
- where to start to place global dataWith these items addressed, Zig is one of the supported languages for the WASM-4 Fantasy Console. Tom, the creator of Context Free, is hosting a game jam on January 14 - 23.
Additionally, the linker frontend for wasm targets now supports the following flags:
--export
: Forces a symbol to be exported to the host environment.-rdynamic
: Adds all symbols to the dynamic symbol table (known as
--dynamic-export
in wasm-ld
).Contributors: Luuk de Gram, Takeshi Yoneda, Jakub Konka, Andrew Kelley
WebAssembly System Interface is a pseudo-operating system that provides sandboxed I/O and other system capabilities to WebAssembly.
wasi_snapshot_preview1
should not generate a link flag
-lwasi_snapshot_preview1
on the linker line as this is not a system library that needs to be
imported at link-time, but a system library that is provided by the runtime at load-time.fd_readdir
right.Zig now has Continuous Integration testing enabled for Linux builds against glibc.
-march
flag in order to communicate CPU features to
Clang when compiling assembly files.Contributors: Andrew Kelley, Michael Dusan, vole-dev
TCHAR
idiom entirely.*W
functions instead of *A
functions (#534).
This eliminates the last and final call to any A functions so now we are properly using only W
functions in the Standard Library.OVERLAPPED
, add OVERLAPPED_ENTRY
.ChildProcess
(#9148).WSAOVERLAPPED
. Use LPWSAOVERLAPPED_COMPLETION_ROUTINE
.GetCurrentDirectory
returns a path with a trailing slash if and only if
the cwd is a root directory, making the code in resolveWindows
return an
invalid path with two consecutive slashes (#10093).Contributors: Andrew Kelley, viri, mchudleigh, Jonathan Marler, Travis Martin, LemonBoy
0.9.0 is the first release which publishes a build of Zig for ARM64 Windows (#9102): zig-windows-aarch64-0.9.0.zip
Be warned that it has not been properly tested yet; Zig developers are still in the process of acquiring the hardware needed to run ARM64 Windows. Thanks to Martin Storsjö for the tip about the ECS LIVA Mini Box QC710.
Contributors: Stephen von Takach, Nameless, Sreehari Sreedev
Thanks to major improvements in the Self-Hosted Linker combined with support for compiling Objective-C, along with miscellaneous improvements in the Self-Hosted Compiler, Zig now targets iOS and iPhone Simulator platforms. See kubkon/zig-ios-example for a complete example.
(#9532)
Initial bringup of the Solaris/Illumos port by Stephen Gregoratto.
Improved Haiku support in the Standard Library by Al Hoang (#10073).
Thanks to waddlesplash for stopping by and reviewing the patch.
Generally, Zig 0.9.0 works much better than 0.8.x on macOS, due to major improvements in the Self-Hosted Linker, Versioned C Headers, and miscellaneous improvements, such as:
_mh_execute_header
(#10217).The fetch-them-macos-headers project has been improved to account for headers from multiple versions of macOS. Currently, these versions are 10 (Catalina), 11 (Big Sur), and 12 (Monterey). As Apple releases new major versions of macOS, this project will track the latest three, which matches the security update policy of Apple.
The tool has been improved to detect which files are different across both versions and architectures, dividing the files up into eight categories:
Files that are common between multiple categories are put into the "any" ones, as least specific as possible, to save on installation size.
Long story short, this fixes bugs having to do with incompatible header files, and comes at a minimal cost of additional installation size:
Before these changes:
After these changes:
fadvise
support.waitid
syscall (#9335).mknod
and mknodat
syscalls.inotify_rm_watch
definition and made
inotify_add_watch
's pathname marked as null-terminated.FUTEX
definitions.clone
/clone3
and missing signalfd
flags.rlimit_resource
for MIPS and SPARC (#9227).EBADLIB
in execve
now maps to error.InvalidExe
rather than error.Unexpected
.Contributors: Andrew Kelley, LemonBoy, Jens Goldberg, Vincent Rischmann, Aydin Mercan, Kenta Iwasaki, Hiroaki Nakamura, Felix "xq" Queißner
epoll_ctl
, poll_add
, and poll_remove
.register_eventfd
, register_eventfd_async
, unregister_eventfd
(#9449).poll_update
. Additionally added a method from liburing to queue
(but not submit) a SQE to update the user data of an existing poll operation.cancel
and io_uring_prep_cancel
(#10081).register_files_update
.link_timeout
(#10151).EVFILT_TIMER
instead of EVFILT_USER
.
OpenBSD doesn't implement EVFILT_USER filter for kqueue(2), so we couldn't use that for
event loop. Instead, use a EVFILT_TIMER filter with EV_ONESHOT (trigger only once) and
delay 0sec (which trigger immediatly). It fits the usage of EVFILT_USER which is only
used to "wakeup" the kevent(2) call from userland.std.c.openbsd
(#10178).Contributors: Sébastien Marie, Dante Catalfamo
restore_rt
. Prevents
infinite loop when calling rt_sigreturn
.freeAndExit()
implementation.Contributors: Koakuma
A new linker backend for the Plan9 a.out executable format has been written for the Self-Hosted Compiler by Jacob G-W. The linker only works with the x86_64 and aarch64 backends because LLVM cannot emit Plan9 object files. Experimental source location debug info is emitted from this linker.
It can be used with -target x86_64-plan9
.
Hopefully Zig can soon join Go as the second modern language that is a first class citizen on Plan9!
People have started to use Zig to build their own operating systems due to the standard library being available and unopinionated.
Some improvements to this use case in 0.9.0:
MAX_PATH_BYTES
for custom operating systems.os/<os>/<arch>.zig
files.root.os.panic
could return.Contributors: N00byEdge, mason1920, rgreenblatt
Contributors: Andrew Kelley, pfg, Mr. Paul, Daniele Cocca, Lee Cannon, Travis Staloch, Jacob G-W, Robin Voetter, Auguste Rame, Meghan, Michael Byrne, David May, Dmitry Matveyev, Evan Haas, Exonorid, Isaac Freund, Jarred Sumner, Jonathan Marler, Josh Soref, Nathan Michaels, Norberto Martínez, Nulo, Paul, Philipp Lühmann, Roman Frołow, Rory O’Kane, Veikka Tuominen, bnprks, kprotty, travisstaloch, yetanothercheer
@byteOffsetOf
is renamed to @offsetOf
. zig fmt automatically performs this upgrade for you.
During this release, a language proposal for pointer address spaces was materialized and partially implemented in the Self-Hosted Compiler. This gives the user the ability to attribute a pointer or variable with a certain address space, after which the compiler will be able to generate specialized instructions to load or store variables to those different address spaces. While this feature is relatively niche, it is an invaluable feature for embedded hardware and graphics processing units, and so broadens the number of targets that Zig can reasonably to support.
To instruct the linker to place a variable within a particular address space
(if supported), or to create a pointer pointing to a value in a particular
address space, one can use the addrspace
keyword.
pub const will_be_placed_in_flash: i32 addrspace(.flash) = 123;
pub fn readFlash(ptr: *addrspace(.flash) i32) i32 {
return ptr.*;
}
Thanks to Robin Voetter for the proposal, specification, and implementation of these changes.
@tagName
, @errorName
,
@typeName
, and @embedFile
now
have a return type of *[N:0]const u8
instead of
[]const u8
. This means that these builtins can be used anywhere
a string literal would be used. In other words, they can be
coerced into
any of these types:
[]const u8
(slice which has pointer and length)[*:0]const u8
(null-terminated pointer)[:0]const u8
(slice which has pointer and length and is also
guaranteed by the type system to have a null byte at the end, which is not counted in the length)Similarly, the file
and fn_name
fields of
std.builtin.SourceLocation
returned by @src
now
are [:0]const u8
, making it possible to pass them as null-terminated
strings without copying them.
New builtins: @minimum and @maximum
These builtins accept integers, floats, and vectors of either. In the latter case, the operation is performed element wise.
NaNs are handled as follows: if one of the operands of a (pairwise) operation is NaN, the other operand is returned. If both operands are NaN, NaN is returned.
See also: SIMD
New builtin: @select
@select(comptime T: type, pred: @Vector(len, bool), a: @Vector(len, T), b: @Vector(len, T)) @Vector(len, T)
Selects values element-wise from a
or b
based on pred
. If pred[i]
is true
, the corresponding element in the result will be a[i]
and otherwise b[i]
.
See also: SIMD
In this release, the following builtins have been improved to support SIMD vectors:
These SIMD-enabled builtins are new in 0.9.0:
SIMD support in Zig pretty far along, with more improvements still planned. If you try to use SIMD in your Zig codebase and you find that it does not satisfy your use case, we want to hear from you! Please open an issue and describe the situation.
SIMD Vector Syntax
is planned so that one is not required to use @Vector
.
Documentation in the language reference is bare bones and needs to be fleshed out.
There is still some missing functionality which you can see listed in #903.
The focus now has shifted to implementing SIMD in the Self-Hosted Compiler since that is ultimately what everyone will be using.
This is a backwards-compatible language change.
Previously, @intToEnum
coerced its integer operand to the integer tag
type of the destination enum type, often requiring the callsite to
additionally wrap the operand in an @intCast
.
Now, the @intCast
is
implicit, and any integer operand can be passed to @intToEnum
.
The same as before, it is illegal behavior to pass any integer which does not have a corresponding enum tag.
In summary, you can change this:
return @intToEnum(Tag, @intCast(@typeInfo(Tag).Enum.tag_type, number));
...into this:
return @intToEnum(Tag, number);
Locals are never allowed to shadow declarations, but declarations will sometimes need to have the same name as each other. Consider this case:
pub const List = struct {
head: *Node,
pub const Node = struct {
bar: i32,
next: *Node,
pub fn init() Node {
// ...
}
};
pub fn init() List {
// ...
}
};
Consider the full namespace paths of the functions:
List.init
List.Node.init
This is completely reasonable and expected, but it causes, within the scope of
Node
, for the init
identifier to be ambiguous. For example,
if we reference the init
function from the inner scope, we get an error:
This is resolved by eliminating the ambiguity by accessing the function via its namespace:
pub const List = struct {
head: *Node,
pub const Node = struct {
bar: i32,
next: *Node,
pub fn init() Node {
// ...
}
test "example" {
_ = Node.init;
_ = List.init;
}
};
pub fn init() List {
// ...
}
};
No error this time.
Similarly, variables are now allowed to have the same names as primitives
such as i32
and null
, however they
must use the @""
sytax in order to disambiguate (#6062).
The motivation for these language decisions is to reduce the amount of non-local awareness a person reading Zig code must have in order to confidently make changes.
Now these are primitives provided by the language, same as void
,
u32
, etc.
Consequently one can now use these tokens as, for example, struct field names:
const Foo = struct {
true: i32,
false: i32,
undefined: i32,
null: i32,
};
However it is still required to use @""
syntax to disambiguate
between references to primitives or same-named identifiers. Example:
This is a breaking change that is likely to affect most codebases.
usingnamespace
no longer imports identifiers into the current
namespace:
It can still be used, however, to add declarations to a namespace. For example,
a file called c.zig
which contains the following:
pub usingnamespace @cImport({
@cInclude("stdio.h");
@cInclude("math.h");
@cInclude("time.h");
@cInclude("epoxy/gl.h");
@cInclude("GLFW/glfw3.h");
@cDefine("STBI_ONLY_PNG", "");
@cDefine("STBI_NO_STDIO", "");
@cInclude("stb_image.h");
});
This was the intended use case for usingnamespace
all along, and
it still works the same.
The motivations for this change are:
Compilation speed is a serious goal of the project. Zig the language is willing to take on restrictions and design limitations in order to facilitate potential compilation speed enhancements.
There is an open proposal to rename the keyword.
c_void
actually has nothing to do with C; it is just an opaque type
that has some relaxed coercion rules surrounding type erasure.
In Zig 0.9.0 it is renamed to anyopaque
but it has the exact
same semantics.
zig fmt automatically performs the rename.
Zig now has syntax to perform arithmetic that does not wrap, overflow, or invoke illegal behavior. Instead, if the result does not fit into the destination type, the value is clamped to the minimum or maximum:
Relevant issues: #9949 #9679 #9619
It is no longer possible to have a local variable or parameter that is not referenced:
This has been planned since before 0.1.0 was released, and it is planned to introduce more compile errors for unused things, despite this having been a controversial change. Zig will never have a "sloppy mode" flag.
The motivation for this change is helping with refactoring and reworking code, and preventing bugs, ultimately saving developer time.
If a local is intentionally unused, it can be discarded, like this:
The eventual goal for the developer experience related to errors for unused things is that it will work similar to the "Organize Imports" button in Eclipse that Java developers are familiar with. Idea being that IDEs would have a simple keyboard shortcut that would automatically add discards for all unused things, eliminating this compile error as a stumbling block when a developer is experimenting, and yet ensuring that the source code on disk documents the code smell with explicit discarding of unused things.
Another possibility is that zig fmt could grow a --fix-unused
flag, performing this hypothetical operation outlined above.
Please note there is an accepted proposal to
add an unused
keyword,
making it possible to detect conflicts between things being marked unused
and actually being used, and improving tooling integration.
@export creates a symbol in the output object file.
Previously, declaration
must be an identifier. Now, it could be
field access expression:
x
) identifying a
function or a
variable.x.y
) looking up a
function or a
variable.The end-game for inline assembly is that the syntax is more integrated with Zig, and it will not allow string concatenation for the assembler code, for the same reasons that Zig does not have a preprocessor.
However, inline assembly in zig right now is lacking for a variety of use cases (take a look at the open issues having to do with inline assembly for example), and being able to use comptime expressions to concatenate text is a workaround that real-world users are exploiting to get by in the short term.
This release introduces "assembly code must use string literal syntax" as a compile error when using the Self-Hosted Compiler, but allows it through when using the Bootstrap Compiler.
New builtin: @prefetch
@prefetch(ptr: anytype, comptime options: std.builtin.PrefetchOptions)
This builtin tells the compiler to emit a prefetch instruction if supported by the target CPU. If the target CPU does not support the requested prefetch instruction, this builtin is a noop. This function has no effect on the behavior of the program, only on the performance characteristics.
The ptr
argument may be any pointer type and determines the memory
address to prefetch. This function does not dereference the pointer; it is perfectly legal
to pass a pointer to invalid memory to this function and no illegal behavior will result.
The options
argument is the following struct:
In the previous release, as well as this release, the main Zig compiler everybody uses is the bootstrap compiler, written in C++, also known as "stage1". Despite the main focus of this release cycle being the Self-Hosted Compiler, there were some improvements to stage1 as well.
@truncate
to an integer type of different sign an error
at comptime too.--verbose-ast
and --verbose-tokenize
(#9034).@shuffle
type and mask parameters to imply a
comptime
scope for the expression.c_longdouble
mapping for the s390x architecture.@mulAdd
directly to a call to fmaq
instead of to the LLVM intristic
because LLVM will lower it to fmal
even when the target's
long double
is not equivalent to f128
.zig test --verbose-air
crashing on an empty file (#10031).c_longdouble
for the
nvptx architecture.-fsingle-threaded
.NO_COLOR
environment variable.@divTrunc
behavior dependent on the order of operands (#10001).Contributors: Andrew Kelley, LemonBoy, Martin Wickham, Robin Voetter, Daniele Cocca, Takeshi Yoneda, Auguste Rame, Daniele Cocca, Jacob G-W, Lee Cannon, LemonBoy, Matthew Borkowski, Travis Staloch, leesongun, travisstaloch, Belhorma Bendebiche, Evan Haas, Exonorid, Frank Denis, Jakub Konka, Jay Petacat, Josh Soref, Kirk Scheibelhut, Meghan Denny, Michael Dusan, Richard Eklycke, Stephen Gregoratto, Veikka Tuominen, mlarouche, vole-dev, Žiga Željko
Main issue: #9046
uwtable
attr when
linking libunwind, or always on Windows. It also makes
link_eh_frame_hdr
true automatically if uwtable
attr is
set to be on for zig functions.-funwind-tables
and -fno-unwind-tables
to allow the user to override the defaults.The Self-Hosted Compiler is so fast that Zig 0.9.0 additionally runs the first three phases of the self-hosted pipeline (tokenizing, Parser, and AST lowering), before invoking the bootstrap compiler, to enable Compile Errors for Unused Locals among other kinds of compile errors.
At first it seemed this would be a small perf hit to additionally run part of the self-hosted compiler in front of the bootstrap compiler, but it would be worth it to gain access to the compile errors that are only implemented in the self-hosted compiler.
But something delightful happened.
This change allowed us to move the "unreachable code" compile error from the bootstrap compiler to the self-hosted compiler. This made it possible to delete a couple of fields from some structs within the bootstrap compiler, resulting in a small performance improvement. But because the self-hosted compiler is so fast, the performance gain outweighed the performance cost!
So we actually improved performance by additionally running the self-hosted compiler in front of the bootstrap compiler.
The main focus of this release cycle was the self-hosted compiler (also known as "stage2").
Here is an infographic to communicate a sense of progress:
Currently we have 467 out of 1072 behavior tests passing with the LLVM Backend. Once this reaches 100% we can start shipping the self-hosted compiler instead of the Bootstrap Compiler.
In this release cycle, we took a detour from making progress on passing more tests, to invest in applying Data-Oriented Design principles to the codebase. This resulted in significant performance improvements.
Despite the fact that .zig source code by default is still compiled in this release using the Bootstrap Compiler, the main driver code is already self-hosted, as well as many features, such as zig cc, C Translation, CPU feature detection, and the Cache System. Improvements made to "stage2" in these areas do in fact affect the main Zig user experience. The bullet points listed here are part of the shared code between both compiler implementations.
Miscellaneous improvements:
--override-lib-dir
to --zig-lib-dir
.
This breaking change disambiguates between overriding the lib dir when
performing an installation with the Zig Build System, and overriding the
lib dir that the Zig installation itself uses.zig ast-check
is now run unconditionally for
all files, even when using the Bootstrap Compiler. This means instant
feedback for many compile errors such as Compile Errors for Unused Locals
(#9191).--sysroot
link option (#9202).NO_COLOR
environment variable.-rdynamic
now implies -fdll-export-fns
unless the
latter is explicitly set (#9340).-femit-llvm-bc
CLI option, and improved -fcompiler-rt
support (#9440).-z origin
, -z noexecstack
,
-z now
, -z relro
, -z notext
, and
-z nodelete
. These are additionally available in the
Zig Build System.zig init-lib
and zig init-exe
will no longer overwrite
src/main.zig
if it already exists.zig init-exe
now demonstrates simple testing (#9919).zig init-lib
and zig init-exe
build.zig const (#10057).zig test
gains a --test-no-exec
flag.zig test --test-evented-io
(#9779).-femit-implib
.-fsingle-threaded
/-fno-single-threaded
(#10143).
--single-threaded
.
Contributors: Andrew Kelley, Jakub Konka, Jacob G-W, Robin Voetter, Joachim Schmidt, Luuk de Gram, Evan Haas, Martin Wickham, Matthew Borkowski, Lee Cannon, Veikka Tuominen, Takeshi Yoneda, Travis Staloch, drew, Ryan Liptak, xackus, Emily Bellows, Jonathan Marler, g-w1, Alex Rønne Petersen, Dmitry Matveyev, J.C. Moyer, Meghan Denny, Michael Dusan, kprotty, Andrew Gutekanst, Daniele Cocca, FnControlOption, Isaac Freund, Kurt Kartaltepe, LemonBoy, Lewis Gaul, Matt Knight, Tom Maenan Read Cutting, Zen1th, jacob gw, vole-dev, Žiga Željko, AODQ, Al Hoang, Christopher Smyth, Dimenus, Exonorid, Frank Denis, Hadrien Dorio, Isaac Freund, Josh Soref, Kenta Iwasaki, Loris Cro, Michal Ziulek, Scibuild, Sebastian Ullrich, Sizhe Zhao, Stephen Gregoratto, Stephen Gutekanst, Stéphan Kochen, Tamas Kenez, Thomas Ives, Vincent Rischmann, daurnimator, pithlessly, travisstaloch, xavier
suspend
expressions now require blocks.The LLVM backend is now passing 44% of the behavior tests. This is currently the main focus of the self-hosted compiler effort, because when it gets to 100%, we can start shipping the self-hosted compiler instead of the Bootstrap Compiler.
The new LLVM backend has reached a milestone: it can now produce a viable binary for this Tetris clone.
This gives us some early performance measurements. On Andrew's laptop:
zig build-exe src/main.zig -fLLVM -lc -Istb_image-2.22 -lglfw -lepoxy --main-pkg-path . stb_image-2.22/stb_image_impl.c -fno-stage1
Using the Bootstrap Compiler:
Using the Self-Hosted Compiler:
This time includes:
So this is not a fair benchmark to measure how many lines/second Zig can compile. Even so, it's 1.5x faster and 0.53x as much memory as the Bootstrap Compiler.
The C backend is now passing 22% of the behavior tests. When it gets to 100%, we can delete the Bootstrap Compiler and replace it with the self-hosted compiler converted into C code.
The x86 backend is not yet capable of executing the zig test
runner,
so technically it is passing 0% of the behavior tests.
However basic codegen is already in place, we have an MIR representation, and both rendering to machine code and assembly code is implemented. We are just a few bug fixes and enhancements away from x86 joining the "behavior tests passing" progress bar.
Jakub Konka has started working on this in earnest.
The aarch64 backend is in a very similar state as the x86 Backend. Joachim Schmidt has been making steady progress.
The main improvements to the self-hosted linker have been to the MachO linker backend by Jakub Konka and to the WebAssembly linker backend by Luuk de Gram.
The most significant milestone for the self-hosted MachO linker is that it is getting battle tested by the community every day, and it can now successfully be used as a drop-in linker for other languages and environments such as C, C++, Objective-C, Objective-C++, Go and Rust. It can also be used to cross-compile code dependent on frameworks from a non-Apple host such as Linux to Apple platforms such as macOS, iOS, etc., with the sysroot provided by the user.
Additionally, the linker was featured at the 2021 Handmade Seattle Conference where Jakub in a short 5 minute video demonstrated the linker's current capabilities when used with the bootstrap compiler (stage1), and showcased a prototype for incremental linking and what developer experience it will unlock when used with the self-hosted compiler (stage2). The demo is available at the Handmade Seattle Conference media website here: The ZLD Linker demo.
Major improvements to the MachO linker backend:
__eh_frame
debug section.libSystem.B.tbd
.-fallow-shlib-undefined
or equivalently
in macOS parlance -Wl, "-undefined dynamic_lookup"
flags. This in turn implies that Zig can be
used to successfully build NodeJS or Python native addons/modules which require deferred until load-time
undefined symbol resolution. See for instance issue #3000.-Dlink-snapshot
, and then the compiler needs to be passed --debug-link-snapshot
.Miscellaneous linker improvements:
N_DESC_DISCARDED
description flag, simply ignore it rather than throw
an error.UndefinedSymbolReference
.ARM64_RELOC_POINTER_TO_GOT
target.UNSIGNED
relocs on arm64.LC_FUNCTION_START
data.-fallow-shlib-undefined
and
-Wl,"-undefined=dynamic_lookup"
flags.
As a result of this change,
it is now possible to generate a valid native Node.js addon with Zig
for macOS (#8180) (#3000).std.macho
, and fix two bugs (#10338).-framework
is not
found.c_uint
type.extern enum
. Instead, translate enum
types as the underlying integer type. Translate enum constants
as top-level integer constants of the correct type (which does not necessarily
match the enum integer type). If an enum constant's type cannot be translated
for some reason, omit it.
discussion
(#9153).@
syntax to
escape _
when used as an identifier.[*]T + isize
is allowed,
this can be removed (#8556).char *
.
In C the type of string literals is char *
, so when using them in
a non-const context we have to cast the const away (#9126).inline fn
for always_inline
.clang::ASTUnit::LoadFromCommandLine
.In addition to these improvements to zig translate-c
,
Veikka Tuominen, Evan Haas, and iddev5 have been working on
arocc, a C compiler written in Zig.
One of the goals of the project is to
replace Clang as Zig's C frontend for C translation.
Contributors: Evan Haas, Veikka Tuominen, xackus, Matthew Borkowski, Stéphan Kochen, Andrew Kelley, Tamas Kenez
Zig's cache system is central to its strategy to support many targets, by shipping with only source files and lazily building artifacts as needed.
The cache system now drops from exclusive locks to shared locks after an artifact is built. Additionally, when checking the cache, if an exclusive lock cannot be obtained, a shared lock will be obtained instead. Together, this solves a design flaw in the caching system that was causing deadlocks, and allows multiple processes to share the same read-only build artifacts, while still allowing a cache garbage collection utility to operate concurrently with the Zig compiler even as build artifacts are being actively used.
This required new Standard Library functionality:
std.fs.File.setLock
. The Windows implementation based on using
NtLockFile
and NtUnlockFile
rather than relying on
ShareAccess
flags and manual polling.
This resolved the deadlock problems reported in #9139 and #9187. It also allowed us to remove another workaround to a deadlock when supplying the same source file multiple times.
Main issue: #7596
All Zig code is eligible to @import("builtin")
which is mapped to a generated file, builtin.zig
, based on the target and other settings.
Zig invocations which share the same target settings will generate the
same builtin.zig
file and thus the path to builtin.zig
is in a shared
cache folder, and different projects can sometimes use the same file.
In previous versions of Zig, this led to conditions where multiple
invocations of zig
would race to write this file. If one process
wanted to read the file while another process wrote the file, the
reading process could observe a truncated or partially written builtin.zig
file.
Zig 0.9.0 has the following improvements:
builtin.zig
file into place,
preventing any race conditions from occurring.builtin.zig
on disk for debug info and stack trace purposes.stat()
syscall that happens in a worker thread.In summary, performance is improved and a race condition crash was fixed (#9439).
One problem with caching systems is that if a file is written, read, and then written again all within a very short period of time, the file system mtime granularity may not tell a difference between those two writes, and so a subsequent read might register an unchanged file, when in fact the file was changed.
To avoid this problem, Zig recognizes the concept of problematic timestamps, which notices at read() time that the current time is so recent that the file system's mtime granularity would not actually tell the difference from the previous write. In such case it marks the mtime as untrustworthy, so that next time the cache system does not rely on the mtime, and is forced to fall back to a file contents hash.
In previous releases of Zig, this problematic timestamp was calculated based on a syscall to get the current time. This was not an accurate measurement of problematic timestamps, because asking the system for the current time did not necessarily correspond to the time that the file system would return, and it had no way of conveying the mtime granularity of the filesystem.
In Zig 0.9.0, the problematic timestamp is learned by writing a temporary file to the cache directory, and reading the mtime from that temporary file. This mtime will have the appropriate granularity to use to check if a timestamp is problematic (i.e. too recent) and should not be trusted.
Thank you to Travis Martin for submitting this improvement (#9930).
Stephen Gutekanst writes:
While investigating slow build times with
a large project,
I found that the compiler was reading from disk nearly every C source file in my project
when rebuilding despite no changes having been made. This accounted for several seconds of
time (approx. 20-30% of running zig build
without any changes to the sources.)
The cause of this was that comparisons of file mtimes would always fail (the mtime of the file on disk was always newer than that stored in the cache manifest), and so the cache logic would always fall back to byte-for-byte file content comparisons with what is on disk versus in the cache - reading every C source file in my project from disk during each rebuild. Because file contents were the same, a cache hit occurred, and despite the mtime being different the cache manifest would not be updated.
One could reproduce this by building a Zig project so the cache is populated, and then changing mtimes of their C source files to be newer than what is in the cache (without altering file contents.)
The fix was rather simple: always write the updated cache manifest regardless of
whether or not a cache hit occurred, because a cache hit doesn't indicate if a manifest
is dirty. Luckily, writeManifest
already contained logic to determine if a
manifest is dirty and becomes no-op if no change to the manifest file is necessary,
so the fix was to call writeManifest
in the Self-Hosted Compiler
even in the case of a cache hit.
Martin Wickham added this crash report to the panic function, which has been really nice for internal compiler development. Crashes now look something like this:
$ ./zig-out/bin/zig test -fLLVM ../test/behavior/eval_stage1.zig
thread 831982 panic: access of inactive union field
Analyzing ../test/behavior/eval_stage1.zig: eval_stage1.zig:test.inlined loop has array literal with elided runtime scope on first iteration but not second iteration
%115 = load(%111) node_offset:22:19
%116 = int(2)
%117 = cmp_lt(%115, %116) node_offset:22:21
%118 = as_node(@Ref.bool_type, %117) node_offset:22:21
> %119 = condbr_inline(%118, {
%127 = dbg_stmt(23, 9)
%128 = alloc_inferred() node_offset:23:9
%133 = block({
%129 = load(%111) node_offset:23:28
%130 = cmp_eq(%129, @Ref.zero) node_offset:23:30
%131 = as_node(@Ref.bool_type, %130) node_offset:23:30
%132 = condbr(%131, {
%134 = array_type(@Ref.one, @Ref.i32_type)
%135 = elem_type(%134) node_offset:23:36
%136 = coerce_result_ptr(%134, %128)
%137 = elem_ptr_imm(%136, 0) node_offset:23:43
%138 = int(2)
%139 = store_node(%137, %138) node_offset:23:43
%140 = validate_array_init({
%137 = elem_ptr_imm(%136, 0) node_offset:23:43
}) node_offset:23:42
%143 = break(%133, @Ref.void_value)
}, {
%141 = load(%101) node_offset:23:51
%142 = store_to_block_ptr(%128, %141)
%144 = break(%133, @Ref.void_value)
}) node_offset:23:24
}) node_offset:23:24
%145 = resolve_inferred_alloc(%128) node_offset:23:9
%146 = dbg_stmt(24, 9)
%147 = load(%128) node_offset:24:13
%148 = ensure_result_non_error(%147) node_offset:24:13
%149 = break_inline(%120, @Ref.void_value)
}, {
%150 = break_inline(%114, @Ref.void_value)
}) node_offset:22:12
For full context, use the command
zig ast-check -t ../test/behavior/eval_stage1.zig
in ../test/behavior/eval_stage1.zig: eval_stage1.zig:test.inlined loop has array literal with elided runtime scope on first iteration but not second iteration
> %120 = block_inline({%115..%119}) node_offset:22:12
in ../test/behavior/eval_stage1.zig: eval_stage1.zig:test.inlined loop has array literal with elided runtime scope on first iteration but not second iteration
> %114 = block_inline({%120..%126}) node_offset:22:12
/home/andy/Downloads/zig/src/Sema.zig:991:53: 0x3049c86 in Sema.analyzeBody (zig)
const break_data = datas[break_inst].@"break";
^
/home/andy/Downloads/zig/src/Sema.zig:956:56: 0x3049525 in Sema.analyzeBody (zig)
const break_inst = try sema.analyzeBody(block, inline_body);
^
/home/andy/Downloads/zig/src/Sema.zig:956:56: 0x3049525 in Sema.analyzeBody (zig)
const break_inst = try sema.analyzeBody(block, inline_body);
^
/home/andy/Downloads/zig/src/Module.zig:4306:25: 0x301b2c5 in Module.analyzeFnBody (zig)
_ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
^
/home/andy/Downloads/zig/src/Compilation.zig:2343:47: 0x2dff765 in Compilation.processOneJob (zig)
var air = module.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {
^
/home/andy/Downloads/zig/src/Compilation.zig:2261:30: 0x2df229d in Compilation.performAllTheWork (zig)
try processOneJob(self, work_item, main_progress_node);
^
/home/andy/Downloads/zig/src/Compilation.zig:1872:31: 0x2deddd7 in Compilation.update (zig)
try self.performAllTheWork();
^
/home/andy/Downloads/zig/src/main.zig:2875:20: 0x2d5812f in updateModule (zig)
try comp.update();
^
/home/andy/Downloads/zig/src/main.zig:2558:17: 0x2ce4d2e in buildOutputType (zig)
updateModule(gpa, comp, hook) catch |err| switch (err) {
^
/home/andy/Downloads/zig/src/main.zig:216:31: 0x2cca5f6 in mainArgs (zig)
return buildOutputType(gpa, arena, args, .zig_test);
^
/home/andy/Downloads/zig/src/main.zig:165:20: 0x2cc9350 in main (zig)
return mainArgs(gpa, arena, args);
^
/home/andy/Downloads/zig/lib/std/start.zig:553:37: 0x3179297 in std.start.callMain (zig)
const result = root.main() catch |err| {
^
/home/andy/Downloads/zig/lib/std/start.zig:495:12: 0x2ccc037 in std.start.callMainWithArgs (zig)
return @call(.{ .modifier = .always_inline }, callMain, .{});
^
/home/andy/Downloads/zig/lib/std/start.zig:460:12: 0x2ccbde2 in std.start.main (zig)
return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
^
Aborted (core dumped)
Here we can see that there is a bug in the compiler where it tried to access the wrong union field. Not only do we get a stack trace pointing at the union field access, but it tells us the source file, line number, and function that the compiler was analyzing when it crashed, it dumps out the ZIR in text form, pointing to the exact instruction that was being analyzed, and dumps out the analysis stack including the ZIR instructions.
In many cases, seeing this information is enough to completely diagnose the problem, without having to resort to a debugger. It additionally makes it easier for contributors to help out, and it increases the chances of error reports to include enough information to reproduce or diagnose the issue.
Previously when using zig run
or zig test
, Zig
would try to guess whether the host system was capable of running the target binaries.
Now, it will always try. If it fails, then Zig emits a helpful warning to explain the
probable cause:
$ uname -a
Linux ark 5.10.81 #1-NixOS SMP Sun Nov 21 12:46:37 UTC 2021 x86_64 GNU/Linux
$ zig run hello.zig -target aarch64-linux
warning: the host system (x86_64-linux.5.10.81...5.10.81-gnu.2.33) does not appear to be capable of executing binaries from the target (aarch64-linux.3.16...5.5.5-musl)
error: the following command failed to execve with 'InvalidExe':
/home/andy/.cache/zig/o/b61b3c9b78433477f6b5c343ce95631f/hello
$ zig run hello.zig -target x86_64-linux-gnu -lc
warning: the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is located at '/nix/store/z56jcx3j1gfyk4sv7g8iaan0ssbdkhz1-glibc-2.33-56/lib/ld-linux-x86-64.so.2', while the target dynamic linker path is '/lib64/ld-linux-x86-64.so.2'. Consider using --dynamic-linker
error: the following command failed to execve with 'FileNotFound':
/home/andy/.cache/zig/o/f0f8bdc473cbcf7f00bd72f0450102ad/hello
$ zig test behavior.zig -target aarch64-linux
warning: the host system (x86_64-linux.5.10.81...5.10.81-gnu.2.33) does not appear to be capable of executing binaries from the target (aarch64-linux.3.16...5.5.5-musl). Consider using --test-no-exec or --test-cmd
error: the following command failed with 'InvalidExe':
../test/zig-cache/o/b70989a95e10772fc12652c7f94a5cf5/test zig
$ zig test behavior.zig -target x86_64-linux-gnu -lc
warning: the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is located at '/nix/store/z56jcx3j1gfyk4sv7g8iaan0ssbdkhz1-glibc-2.33-56/lib/ld-linux-x86-64.so.2', while the target dynamic linker path is '/lib64/ld-linux-x86-64.so.2'. Consider using --dynamic-linker, --test-no-exec, or --test-cmd
error: the following command failed with 'FileNotFound':
../test/zig-cache/o/795162652b84a90baa06fbb62db5eb68/test zig
This integrates better when something such as binfmt_misc is installed.
See also the new Executors feature of the Zig Build System.
Now, the default for dynamic libraries (-l
or
--library
) is to only link them if they end up being actually used.
With the Zig CLI, the new options -needed-l
or --needed-library
can be used to force link against a dynamic library.
With zig cc
, this behavior can be overridden with
-Wl,--no-as-needed
(and restored with -Wl,--as-needed
).
We might need to revert this change in order to satisfy the design requirement of having the default be the same on all platforms due to some macOS considerations.
Related issue: #10164
The Zig standard library is still unstable and mainly serves as a testbed for the language. After the Self-Hosted Compiler is completed, the language stabilized, and Package Manager completed, then it will be time to start working on stabilizing the standard library. Until then, experimentation and breakage without warning is allowed.
Miscellaneous improvements:
SectionHeaderIterator().next()
to bswapAllFields()
(#9014).io.FixedBufferStream
seekTo (#9023).ChildProcess
exit code u8
to match
process.exit
.os.dup()
.copy_file_range
checks run at compile-time (#9146).os
functions.fs.path.join
: skip empty strings.ArrayList(u0)
works now. This type still tracks length,
but does not allocate additional memory.DynamicBitSet.iterator
takes self as const
.mem.split
and mem.tokenize
generic instead of assuming
u8
(#9531).Ip4Address
parser: reject 0-prefixed components (#9538).rand.Random
: add enumValue()
(#9583).debug.LineInfo.deinit
and debug.SymbolInfo.deinit
are now public.iov.len > IOV_MAX.
WIFSTOPPED
for Linux, FreeBSD, and
DragonFlyBSD. The intermediate value can be larger than an u16
,
so @truncate
is needed to match the behavior of
musl.ArrayListUnmanaged
: implement writer().ArrayListUnmanaged.allocatedSlice
to match
ArrayList
.net.Address
: Fix writing 0-bytes when formatting Unix addresses.unicode
: cleanup allocations on error in allocating functions. Fixes
leaks when utf16leToUtf8Alloc
/utf16leToUtf8AllocZ
/utf8ToUtf16LeWithNull
return an error and adds relevant test cases.elf
: added a couple missing special section indexes.elf
: add amd64 relocation types.mem.Allocator.resize()
for non u8
element types (#9806).os.flock
: FreeBSD can return EOPNOTSUPP
.@panic
hierarchy by always using
builtin.panic
.deflate
: fixed bits_left
overflow at EndOfStream
and @intCast
truncation with empty Huffman table.deflate
: check for distances past beginning of output stream.deflate
: better Huffman.construct errors and error handling (#9880).os
: handle ETXTBSY
from open()
.PriorityQueue
: moved compareFn
from init to type
constructor in PriorityQueue
and PriorityDequeue
.
This change significantly improves performance for simple compare functions and modifies
the API to be more consistent with e.g. HashMap
.MultiArrayList
: get function take self by value.meta.trait.isContainer
true for opaques.GeneralPurposeAllocator
: fixed memory limit accounting for large
allocations.ArrayList
: add ensureTotalCapacityPrecise
(#10077).ppoll
: cast number of fds
to nfds_t
.Allocator.reallocBytes
. This is useful when dealing with
runtime-known alignments, eg. interfacing with C code that accepts custom allocation
callbacks (#9394).chmod
and chown
.builtin.StackTraceformat
: fix compile error when building for a target
that does not have stderr or detectTTYConfig
(such as freestanding).NotLink
error (#9877) (#9872).u8
in writeIntSlice
.mem.indexOfPos
now returns start_index
when needle length is
zero (#10220) (#10216).system.preadMin
.rmdirZ
EINVAL
error (#10145).PriorityQueue
: comparator now takes a context parameter (#10342).Contributors: Andrew Kelley, Robin Voetter, Jakub Konka, kprotty, Ryan Liptak, Lee Cannon, Vincent Rischmann, LemonBoy, Veikka Tuominen, Jacob G-W, Jonathan Marler, Matthew Borkowski, Evan Haas, Felix (xq) Queißner, Takeshi Yoneda, Koakuma, Luuk de Gram, Martin Wickham, Aaron Sikes, Al Hoang, Frank Denis, Isaac Freund, Marc Tiehuis, lucky, Kenta Iwasaki, Matt Chudleigh, Stephen Gregoratto, Travis Staloch, Ali Chraghi, Asherah Connor, Hiroaki Nakamura, Jens Goldberg, Lewis Gaul, Meghan, Michael Dusan, N00byEdge, Ominitay, Tom Maenan Read Cutting, jacob gw, viri, Dmitry Matveyev, Gregory Anders, InKryption, Isaac Freund, Jarred Sumner, Jeremy Fillingim, Malcolm Still, Samadi van Koten, Silver, Sizhe Zhao, Sreehari Sreedev, Stephen von Takach, Sébastien Marie, Travis Martin, codic12, daurnimator, rgreenblatt, xackus, Adam C, Andrew Gutekanst, Arnav Singh, ArtixFox, Asa Zeren, Austin Clements, Aydin Mercan, Ayende Rahien, Biolunar, Björn Linse, Boo, Carlos Zúñiga, Chris Gregory, Chris Heyes, Coleman Broaddus, Daniele Cocca, Dante Catalfamo, Dante Catalfamo, Dustin Taylor, Edward Dean, Ellis Trisk-Grove, Emil Lerch, Exonorid, Fabio Arnold, Felix "xq" Queißner, Filippo Casarin, FnControlOption, Frank Denis, Garrett Squire, Hiroaki Nakamura, HugoFlorentino, Isaac Freund, Isaac Yonemoto, Jakub Dupak, Jan200101, Justin Whear, Kirjastonhoitaja, Klecko, LemonBoy, Luna, Mahdi Khanalizadeh, Matt Knight, Max Hollmann, Michal Ziulek, Miles Alan, Nameless, Nathan Michaels, Nguyễn Gia Phong, Philip Åkesson, Philipp Lühmann, Rohlem, Sebastien Marie, Tau, Thom Chiovoloni, Tizoner, Trioct, William Stein, Zach Banks, Zapolsky Anton, charlieman, chwayne, d18g, fn ⌃ ⌥, g-w1, hadroncfy, jdmichaud, joachimschmidt557, leesongun, mason1920, pfg, protty, purringChaos, tgschultz, tjohnes, viri
ensureCapacity
is removed in favor of
ensureTotalCapacity
and
ensureUnusedCapacity
.std.Target.current
is moved to
@import("builtin").target
(#9388) (#9321).DynamicBitSet
functions have allocator parameters moved to be the
first one (after self) to match common convention (#9944).debug.warn
is removed in favor of debug.print
.mem.dupe
is removed in favor of mem.Allocator.dupe
.mem.spanZ
is removed in favor of mem.sliceTo
.ensureCapacity
is removed in favor of ensureUnusedCapacity
or
ensureTotalCapacity
.ArrayList.toUnmanaged
is removed in favor of ArrayList.moveToUnmanaged
which has different semantics.base64.standard_pad_char
is removed in favor of base64.standard.pad_char
.base64.standard_encoder
is removed in favor of base64.standard.Encoder
.base64.standard_decoder
is removed in favor of base64.standard.Decoder
.build.Version
is removed; use std.builtin.Version
instead.build.Target
is removed; use std.zig.CrossTarget
instead.hash_map.DefaultMaxLoadPercentage
is removed in favor of hash_map.default_max_load_percentage
.io.FindByteOutStream
is removed in favor of io.FindByteWriter
.io.findByteOutStream
is removed in favor of io.findByteWriter
.mem.alignPointerOffset
mem.alignPointer
Thread.Futex
(#9070).unicode.fmtUtf16le
io.Reader.readUntilDelimiter
BoundedArray
: a simple way to represent small data whose max size is known (#9134).meta.Float
writer
methods on all crypto.hash types (#10168).tanh
for negative inputs (#9047).f128
implementations of
fma
, frexp
, and ilogb
.asinh64
doesn't respect signedness for negative values (#9940).exp2
(#9999).exp64
.ldexp
and make scalbn
an alias.
We assume we are compiled on a base-2 radix floating point system. This
is a reasonable assumption. musl libc as an example also assumes this.
We implement scalbn
as an alias for ldexp
, since
ldexp
is defined as 2 regardless of the float radix.
This is opposite to musl which defines scalbn
in terms of ldexp
(#9799).sin
/cos
/tan
based on
musl (#10276).phi
constant.parse
: added support for recursive objects (#9307).stringify
: added an option to not write out null optional
fields (#8979).fmt.fmtDurationSigned
.Please note that the formatted printing API is unstable and will very likely break everyone's formatting code before we reach 1.0.
@call(.{}, function, args_tuple)
builtin.wait()
to join()
.handle()
to getHandle()
for consistency.detach()
thread, allowing it to clean up
resources when it exits instead of calling join()
.getCurrentThreadId()
into getCurrentId()
.Thread.setName
and Thread.getName
(#8570)
including for DragonFlyBSD (#9910).Thread.Mutex
: change API to lock()
and unlock()
.
This is a breaking change. Before, usage looked like this:
const held = mutex.acquire();
defer held.release();
Now it looks like this:
mutex.lock();
defer mutex.unlock();
The Held
type was an idea to make mutexes slightly safer by making it
more difficult to forget to release an aquired lock. However, this
ultimately caused more problems than it solved, when any data structures
needed to store a held mutex. Simplify everything by reducing the API
down to the primitives: lock()
and unlock()
.
Related issues: #8051 #8246 #10105
The mem.Allocator
interface has changed in a breaking way.
In short summary, here is how to change your code:
*Allocator
to Allocator
in
function parameter types and struct field types.&gpa.allocator
, use a function call like this: gpa.allocator()
.The motivation for this change was performance.
This blog post is an excellent piece of technical writing; I couldn't have done a better job myself in these release notes: Allocgate is coming in Zig 0.9, and you will have to change your code
The rand.Random
interface was changed in an equivalent manner
to the Allocator interface changes, for the same reason (perf).
void
field.HashMap.getOrPutAssumeCapacityAdapted
now sets key to
undefined
(#9138).ensureUnusedCapacity()
over-allocating (#9365).hasUniqueRepresentation
for vectors (#9333).ArrayHashMap.popOrNull
, corresponding to
ArrayList.popOrNull
.getKey
methods (#9607).HashMap.removeAssertDiscard()
/
HashMap.putAssumeCapacity()
no longer cause hash maps to run out of
memory (#7468).${jndi:ldap://...
inside any logged string.root.log
is not a function.std.log.defaultLog
(#9224).std.log.Level.asText
for getting a printable string from the log
level.Over the last year of using std.log
in practice, it has become clear
that having the previous 8 distinct log levels (to match syslog) does more harm than
good. It is too subjective which level a given message should have which
makes filtering based on log level weaker as not all messages will have
been assigned the log level one might expect.
Instead, more granular filtering should be achieved by leveraging the logging scope feature. Filtering based on a combination of scope and log level should be sufficiently powerful for all use-cases.
Note that the Self-Hosted Compiler has already limited itself to 4 distinct log levels for many months and implemented granular filtering based on both log scope and level. This has worked very well in practice.
The four log levels are:
err
- Error: something has gone wrong.
This might be recoverable or might be followed by the program exiting.warn
- Warning: it is uncertain if something
has gone wrong or not, but the circumstances would be worth investigating.info
- Info: general messages about the
state of the program.debug
- Debug: messages only useful for debugging.Since usingnamespace No Longer Affects Identifier Lookup, it helped to introduce proper namespacing to the many OS-specific constants that were essentially ported over from C headers (#9618).
errno
is now a nonexhaustive enum, with the 'E' prefix chopped off.Full list of the 147 bug reports closed during this release cycle.
Note: many bugs were both introduced and resolved within this release cycle.
Zig has known bugs and even some miscompilations.
Zig is immature. Even with Zig 0.9.0, working on a non-trivial project using Zig will likely require participating in the development process.
When Zig reaches 1.0.0, a new requirement for Tier 1 Support will be 0 known bugs for that target.
A 0.9.1 release is planned.
The Zig Build System is invoked via the zig build
command, which executes
a declarative build.zig
script to collect options and describe the graph
of steps, and then provides options to execute those steps.
Although it is already essential to nearly every Zig project, the Zig Build System is still experimental and unstable. As a build system, stability is especially important, but cannot occur until the language stabilizes. Language stability is the next area of focus on the Roadmap.
Miscellaneous improvements:
zig build
flag, --prominent-compile-errors
,
which, in the case when there are compile errors, hides all the other standard error output
besides the compile errors (#8513).undefined
,
preventing some common bugs (#9013) (#8928) (#7991).linkLibCpp
helper to std.build.LibExeObjStep
.defineCMacro
to take separate name and value arugments.std.build.InstallDir
: made dupe()
a public function.standardTargetOptions
(#8060).std.build.LibExeObjStep
: linkage
is now an optional that defaults
to null
. Rather than duplicating the compiler's logic to determine
whether to link statically or dynamically by default, the build system simply omits the
flag unless overrridden.zig build
gains new flags: --prefix-lib-dir [path]
: Override default library directory path--prefix-exe-dir [path]
: Override default executable directory path--prefix-include-dir [path]
: Override default include directory path--libc [file]
: Provide a file which specifies libc paths.
This new option sets a default libc paths file to be used for all
LibExeObjSteps
. Setting LibExeObjStep.libc_file
overrides this default. This is required to allow users to cross compile
projects linking system libraries without needing to patch the build.zig.--debug-log [scope]
: Enable debugging the compiler.--override-lib-dir
is renamed to --zig-lib-dir
to disambiguate
with --prefix-lib-dir
.-Dcpu
. Previously, -Dcpu
was
completely ignored if -Dtarget
was not passed as well. Further,
-Dcpu=baseline
was ignored even if -Dtarget=native
was passed.
These two issues are now fixed; zig build
always respects the
-Dcpu
option if present.RunStep
now distinguishes between unexpected child process exit code
and unclean exit.RunStep
gains a print field, which controls whether it prints the
command before running it. By default it is set to builder.verbose
.each_lib_rpath
, which corresponds to
-feach-lib-rpath
and -fno-each-lib-rpath
(#8800).InstallRawStep
: now properly handles empty segments.InstallRawStep
: added Intel HEX support (#9636).InstallRawStep
: allow custom dest_dir
.InstallRawStep
: added single section dumping and padding.Contributors: Andrew Kelley, Felix (xq) Queißner, Lee Cannon, Aaron Sikes, Jonathan Marler, Isaac Freund, Jakub Konka, Martin Wickham, Evan Haas, Ryan Liptak, Tom Maenan Read Cutting, Veikka Tuominen, Andrew Gutekanst, Asa Zeren, Edward Dean, Ellis Trisk-Grove, Jacob G-W, Jan200101, Jarred Sumner, Kenta Iwasaki, LemonBoy, Luuk de Gram, Matt Knight, N00byEdge, Ominitay, Takeshi Yoneda
There has been a breaking change which breaks all projects using
std.build.Pkg
.
The build system now uses std.build.FileSource
in more places instead
of paths. This supports the use case of using generated files anywhere the build
system expects a file, such as when creating an executable or a library.
Usage example:
const std = @import("std");
const TemplateStep = @import("src/TemplateStep.zig");
pub fn build(b: *std.build.Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
// An arbitrary step that generates a output file
const template_step = TemplateStep.create(b, std.build.FileSource{
.path = "example/layout.ztt",
});
const exe = b.addExecutable("demo", "example/main.zig");
exe.addPackage(std.build.Pkg{
.name = "template",
.path = template_step.getFileSource(), // we can now add the generated file also as a package source
});
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
}
Previously, there was a function addBuildOption
which could be used
to create a basic build_options.zig file which could then be imported into zig code
and used for conditional compilation. This API had several problems:
zig-cache/build_options.zig
.Now this API is reworked (this is a breaking change) with the following improvements (#9623):
Example usage:
const client = b.addSharedLibrary("client", "src/client/zig/client_main.zig", .unversioned);
client.setTarget(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
});
client.addPackagePath("shared", "src/shared/index.zig");
const server_options = b.addOptions();
server_options.addOptionArtifact("client_wasm_path", client);
server_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
server_options.addOption(bool, "support_mp3", support_mp3);
const server = b.addExecutable("groovebasin", "src/server/server_main.zig");
server.setTarget(target);
server.setBuildMode(mode);
server.addPackagePath("shared", "src/shared/index.zig");
server.addOptions("build_options", server_options);
server.install();
In this example, we see that the server_main.zig file will be able to
@import("build_options").client_wasm_path
and have a
comptime
string that contains the file path of the
client
WebAssembly file produced by Zig.
Additionally, there were various bug fixes for addOption
(#10016).
zig build
now has the following additional flags:
-fdarling, -fno-darling Integration with system-installed Darling to
execute macOS programs on Linux hosts
(default: no)
-fqemu, -fno-qemu Integration with system-installed QEMU to execute
foreign-architecture programs on Linux hosts
(default: no)
--glibc-runtimes [path] Enhances QEMU integration by providing glibc built
for multiple foreign architectures, allowing
execution of non-native programs that link with glibc.
-frosetta, -fno-rosetta Rely on Rosetta to execute x86_64 programs on
ARM64 macOS hosts. (default: no)
-fwasmtime, -fno-wasmtime Integration with system-installed wasmtime to
execute WASI binaries. (default: no)
-fwine, -fno-wine Integration with system-installed Wine to execute
Windows programs on Linux hosts. (default: no)
These enable integration with system-installed programs which can provide the ability to test cross-compiled build artifacts. For example:
$ uname
Linux
$ uname -m
x86_64
$ zig init-exe
info: Created build.zig
info: Created src/main.zig
info: Next, try `zig build --help` or `zig build run`
$ zig build test # run the tests natively
All 1 tests passed.
$ zig build test -Dtarget=aarch64-linux # compiles but does not run the tests
$ zig build test -Dtarget=aarch64-linux -fqemu # now it runs the tests!
All 1 tests passed.
$ zig build test -Dtarget=x86_64-windows -fwine # we can even test our windows code on linux 😎
All 1 tests passed.
This release of Zig upgrades to LLVM 13.
Good news: we noticed something like a 12% improvement in compilation speed when we upgraded Zig from LLVM 12 to 13.
Zig ships with the source code to musl. When the musl C ABI is selected, Zig builds static musl from source for the selected target. Zig also supports targeting dynamically linked musl which is useful for Linux distributions that use it as their system libc, such as Alpine Linux.
Musl has not tagged a new release since Zig 0.8.0, so the version shipped with Zig remains at 1.2.2. However, there has been a major improvement when targeting dynamically linked musl libc.
Zig now generates a more accurate libc.so for cross compiling purposes.
Previously, the way it worked is that we used
a script
to process the output of objdump --dynamic-syms /path/to/musl/libc.so
and generate a libc.s file, which could then be lazily compiled by Zig into libc.so for the
requested target when cross compiling.
The problem is that the input libc.so file we used was musl built dynamically for x86_64. While this is almost correct for other architectures besides x86_64, it is not completely correct, leading to bugs such as #8896.
The updated script now inputs musl built for 7 different architectures:
These are produced by passing zig cc in as the CC
environment variable when building musl
(instructions).
Next, the script identifies:
Using this information, the script creates a libc.S file, which is shipped along with the Zig compiler. The file is only 104 KB because it uses the preprocessor in order to support the few differences between architectures. For example, if you execute Zig like this:
zig build-exe hello.c -target aarch64-linux-musl -dynamic
Zig will, under the hood, assemble libc.S with -DPTR64 -DARCH_aarch64
,
producing a libc.so file that can be passed to the linker, accurately modeling the libc.so
that will be found on the target.
The preprocessor directives in libc.S are based on excluding symbols, so even when targeting an architecture that is missing from that list of 7, will work fine; it's just that there may be extra symbols that shouldn't be there, and if there happened to be any special symbols that only existed for that one architecture, those will be missing. It will still be a small improvement to expand that list of 7 to more targets. Regardless, the changes made in this release are strictly an improvement when compared with prior status quo.
Zig gains the ability to target glibc 2.34 in addition to the other 46 glibc versions.
There was a major improvement to targeting glibc when cross compiling.
Zig supports targeting every version of glibc for any target architecture. Previously, in order to accomplish this, Zig had only the information from the .abilist files from the latest verison of glibc:
$ cat ../lib/libc/glibc/*.txt | wc -c
205219
$ cat ../lib/libc/glibc/*.txt | xz | wc -c
22976
The information required to do this took up 200 KB installation size / 22 KB tarball size, and it had the following problems (which caused several bugs):
Now there is a new ziglang/glibc-abi-tool that produces an improved dataset. The project is quite interesting; if you are curious to learn more, check out the README of that repository which explains in detail the strategy and encoding.
With the new dataset:
$ cat abilists | wc -c
169421
$ cat abilists | xz | wc -c
24880
The new data that Zig ships with is 165 KB installation size / 24 KB tarball size, and all the above issues are solved.
When a Zig user requests to cross compile for glibc, Zig uses this abilists
file to create, for the requested glibc version and CPU architecture:
...which are then assembled into:
These files are cached and placed on the linker line when cross compiling. Thanks to this improved dataset, they now accurately model the requested version of glibc.
If we naively shipped every version of the .abilist files:
$ cat (find glibc/ -name "*.abilist") | wc -c
37041906
$ cat (find glibc/ -name "*.abilist") | xz | wc -c
205036
...it would have been 35 MiB installation size, 200 KB tarball size. So we have effectively achieved a compression ratio of 219:1 by implementing a bespoke encoding of this information.
When Zig is instructed to build for the native OS (which is the default, unless
a -target
argument is provided) on a glibc system, Zig first
checks its own executable ELF file to determine if it is dynamically linked.
If it is, it introspects its own dynamically loaded libraries to find libc,
and find out if it is glibc, and if so, what version it is.
When the Zig compiler is statically linked, it falls back to inspecting
the /usr/bin/env
ELF file to determine the native glibc version,
by checking the DT_RUNPATH
, and then calling readlink()
on the libc.so
file, because typically the symlink will have e.g.
libc-2.33.so
in the name, revealing the glibc version.
Until now, if the file did not have a DT_RUNPATH
, Zig would give
up and fall back to the default version of glibc, which was 2.17.
Fortunately, this information is also in readlink()
of
ld.so
, which is available as the "INTERP" file path in the ELF file.
Zig now looks for e.g. ld-2.33.so
on the symlink data for the
dynamic linker, which makes Zig correctly detect the native glibc version in
more cases.
In theory a more complete solution would also look at /etc/ld.so.cache
if necessary, and finally fall back to some hard coded paths, in order
to resolve the location of libc.so
, in order to do this readlink()
trick on the resulting path. You can find that flow chart with man ld.so
.
But it looks like this logic will be enough to get a correct answer in all real
world cases. Of course if we find out that is not the case, we can expand the detection
logic.
This has been tested on Debian Buster and glibc-based Void Linux.
See #6469 for more details.
Zig ships with the source code to mingw-w64. When targeting *-windows-gnu and linking against libc, Zig builds mingw-w64 from source for the selected target.
The mingw-w64 project has not tagged a new release since Zig 0.8.0, so the version shipped with Zig remains at 9.0.0. However, there have been improvements related to mingw-w64 integration:
_fseeki64
and _ftelli64
(#9402) (#9766).frexp()
segfault on
Windows.Fixed unwinding through libunwind stack frames (#9591).
Disabled redundant new/delete definitions which are already provided by libcxxabi.
=
such as -Wl,-rpath=foo
.
Update existing args --major-os-version
, --minor-os-version
,
--major-subsystem-version
and --minor-subsytem-version
to work with the new parsing. Also handle -Wl,--script
in addition to
-Wl,-T
.-S
and -emit-llvm
CLI parameters (#6425).-ffunction-sections
, -fcolor-diagnostics
, and
-fcaret-diagnostics
integration (#6290).-Bdynamic
and -Bstatic
integration (#10050).-m
and -mno-
CPU feature flags (#9196).Please be aware that there are still open zig cc issues. However, these are use cases that never worked in the first place, making them bugs and enhancements, not regressions.
Contributors: Andrew Kelley, Vincent Rischmann
zig c++
is equivalent to zig cc with an added -lc++
parameter, but I made a separate heading here because I realized that some people are
not aware that Zig supports compiling C++ code and providing libc++ too!
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
$ zig c++ -o hello hello.cpp
$ ./hello
Hello World!
Cross-compiling too, of course:
$ zig c++ -o hello hello.cpp -target riscv64-linux
$ qemu-riscv64 ./hello
Hello World!
One thing that trips people up when they use this feature is that the C++ ABI is not stable across compilers, so always remember the rule: You must use the same C++ compiler to compile all your objects and static libraries. This is an unfortunate limitation of C++ which Zig can never fix.
--ast-check
now works in combination with --stdin
.Contributors: Jacob G-W, jdmichaud, tjohnes, chwayne
Fixed argument forwarding to LLVM on Windows, fixing zig ar
on Windows.
Zig now supports compiling Objective-C files:
Foo.h
#import <Foundation/Foundation.h>
@interface Foo : NSObject
- (NSString *)name;
@end
Foo.m
#import "Foo.h"
@implementation Foo
- (NSString *)name
{
NSString *str = [[NSString alloc] initWithFormat:@"Zig"];
return str;
}
@end
test.m
#import "Foo.h"
#import <assert.h>
int main(int argc, char *argv[])
{
@autoreleasepool {
Foo *foo = [[Foo alloc] init];
NSString *result = [foo name];
assert([result isEqualToString:@"Zig"]);
return 0;
}
}
$ zig run test.m Foo.m -I. -framework Foundation
Of course the zig cc CLI works as well:
$ zig cc -o test test.m Foo.m -I. -framework Foundation
Likewise, Zig now supports compiling Objective-C++ files. In our example, if we rename test.m to test.mm, Foo.m to Foo.mm, and update test.mm like so:
test.mm
#import "Foo.h"
#import <assert.h>
int main(int argc, char *argv[])
{
@autoreleasepool {
Foo *foo = [[Foo alloc] init];
NSString *result = [foo name];
std::cout << "Hello from C++ and " << [result UTF8String];
assert([result isEqualToString:@"Zig"]);
return 0;
}
}
$ zig run test.mm Foo.mm -I. -framework Foundation
Hello from C++ and Zig
Of course the zig c++ CLI works as well:
$ zig c++ -o test test.mm Foo.mm -I. -framework Foundation
Contributors: Stephen Gutekanst, Jakub Konka, Andrew Kelley
compiler-rt is the library that provides, for example, 64-bit integer multiplication for 32-bit architectures which do not have a machine code instruction for it. In the GNU world, it's called libgcc.
Unlike most compilers, which depend on a binary build of compiler-rt being installed alongside the compiler, Zig builds compiler-rt on-the-fly, from source, as needed for the target platform. This release saw some improvements to Zig's compiler-rt implementation (#1290).
__clzdi2
and __clzti2
.__clzdi2
and __clzti2
.__ffssi2
, __ffsdi2
and __ffsti2
(#10268).__paritysi2
, __paritydi2
, __parityti2
.__ctzsi2
, __ctzdi2
and __ctzti2
.__bswapsi2
, __bswapdi2
and __bswapti2
.__cmpsi2
, __cmpdi2
, __cmpti2
.__ucmpsi2
, __ucmpdi2
, __ucmpti2
.__popcountsi2
, __popcountdi2
, and
__popcountti2
.__mulosi2
.__negsi2
, __negdi2
, __negti2
.__chkstk
, __divti3
, __modti3
, __udivti3
, __umodti3
__isPlatformVersionAtLeast
which are used for
Objective-C and Objective-C++ @available expressions) on
macOS (#10232).With all these additions, we nearly have a fully complete compiler-rt implementation!
Contributors: Andrew Kelley, Jan Philipp Hafer, Jacob G-W, Jonathan Marler, LemonBoy, J.C. Moyer, Kenta Iwasaki, Stephen Gutekanst, Veikka Tuominen, jdmichaud
The gotta-go-fast repository contains a set of benchmarks that are now run for each commit on our Continuous Integration server, providing a set of graphs for various metrics that we track over time.
These have already helped diagnose performance regressions. As an example, the pull request that implemented Allocgate accidentally deleted a call to free() in the GeneralPurposeAllocator. This did not actually cause any tests to fail, but on the performance dashboard, it was clear that a bunch more memory was getting used.
As Zig matures, we will add more benchmarks to this system, particularly for measuring compilation speed of various users' projects, so that we can pay attention to how changes to the codebase over time are affecting performance characteristics.
Thank you to Austin Rude for improving the user interface and display of the graphs.
Zig Software Foundation is now paying Hetzner for a 100 euros/month bare metal x86_64 machine which operates ci.ziglang.org.
We switched away from Azure for x86_64 Linux CI runs, gaining the following benefits:
Big, big thank you to Michael Dusan who volunteered his time to set up and maintain this. Please consider donating to the ZSF so that we can pay more people like him for their valuable time.
Still on the to-do list is migrating these over to the new system via QEMU or some other OS virtualization software:
And finally we still have these remaining:
Unfortunately until we solve these last three, we will continue to experience flaky CI failures due to OOM. That is, until we ship the Self-Hosted Compiler, which uses significantly less memory, side-stepping this problem.
Other miscellaneous news:
The primary goal of the 0.9.0 release cycle was self-hosting the compiler. At this time, 44% of the behavior tests are passing, with the percent rising quickly.
The major theme of the 0.10.0 release cycle will be stabilizing the language, creating a first draft of the language specification, and self-hosting the compiler.
Some upcoming milestones in the next release cycle:
Here are the steps for Zig to reach 1.0:
Having a package manager built into the Zig compiler is a long-anticipated feature. Zig 0.9.0 does not have this feature.
If the package manager works well, people will use it, which means building Zig projects will involve compiling more lines of Zig code, which means the Zig compiler must get faster, better at incremental compilation, and better at resource management.
Therefore, the package manager depends on finishing the Self-Hosted Compiler, since it is planned to have these improved performance characteristics, while the Bootstrap Compiler is not planned to have them.
If you want more of a sense of the direction Zig is heading, you can look at the set of accepted proposals.
Special thanks to those who sponsor Zig. Because of you, Zig is driven by the open source community, rather than the goal of making profit. In particular, these fine folks sponsor Zig for $50/month or more: