Zig is a general-purpose programming language designed for robustness, optimality, and maintainability. Zig is aggressively pursuing its goal of overthrowing C as the de facto language for system programming. Zig intends to be so practical that people find themselves using it, because it "just works".
This release features 6 months of work and changes from 67 different contributors, spread among 1541 commits.
Special thanks to my sponsors who provide financial support. You're making Zig sustainable.
Table of Contents §
- Table of Contents
- LLVM 9
- Support Table
- Language Changes
- usingnamespace
- External Thread Local Variables
- @hasField and @hasDecl
- C Pointers Support Optional Syntax
- Switching on Error Sets
- Bit Manipulation Builtin Functions
- Undeclared Identifiers in Compile-Time Dead Branches
- Default Struct Field Values
- Array Literal Syntax
- @import("root")
- @mulAdd
- Builtins for Math Functions
- Result Location Semantics
- @unionInit
- Unicode Escapes
- Async Functions
- SIMD
- Alignment of Struct Fields
- @Type
- Variable Declarations as Methods
- Standard Library
- Documentation
- zig build
- zig fmt
- libc
- C Translation
- compiler-rt
- Test Coverage
- Miscellaneous Improvements
- Bug Fixes
- Roadmap
- Active Open-Source Projects Using Zig
- Funding Status
- Thank You Sponsors!
LLVM 9 §
This release of Zig upgrades to LLVM 9. Zig operates in lockstep with LLVM; Zig 0.5.0 is not compatible with LLVM 8.
Notably this means that Zig now has RISC-V Support.
Zig also gains emscripten as a target OS. emscripten cannot self-host yet, but when it can, it will be interesting to explore this as an option for a Zig-in-the-browser sandbox, using WebAssembly.
Support Table §
A support table for master branch can be found on the home page. Here the support table for 0.5.0 is reproduced:
| free standing | Linux 3.16+ | macOS 10.13+ | Windows 7+ | FreeBSD 12.0+ | NetBSD 8.0+ | UEFI | WASI | Android | |
|---|---|---|---|---|---|---|---|---|---|
| x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 | N/A | Tier 2 |
| wasm32 | Tier 2 | N/A | N/A | N/A | N/A | N/A | N/A | Tier 2 | N/A |
| arm64 | Tier 2 | Tier 2 | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | N/A | Tier 2 |
| arm32 | Tier 2 | Tier 2 | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | N/A | Tier 2 |
| mips32 LE | Tier 2 | Tier 2 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| i386 | Tier 2 | Tier 3 | Tier 4 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | N/A | Tier 2 |
| bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| mips32 BE | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| mips64 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| powerpc32 | Tier 3 | Tier 3 | Tier 4 | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| powerpc64 | Tier 3 | Tier 3 | Tier 4 | N/A | Tier 3 | Tier 3 | N/A | N/A | N/A |
| wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | Tier 4 | N/A |
| avr | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | N/A | N/A |
| riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | N/A | N/A |
| xcore | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| nvptx | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| msp430 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| r600 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| arc | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| tce | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| le | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| amdil | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| hsail | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| spir | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| kalimba | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| shave | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
| renderscript | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | N/A | N/A |
Tier System §
Tier 1 Support §
- Not only can Zig generate machine code for these targets, but the standard library cross-platform abstractions have implementations for these targets. Thus it is practical to write a pure Zig application with no dependency on libc.
- The CI server automatically tests these targets on every commit to master branch, and updates the download page with links to pre-built binaries.
- These targets have debug info capabilities and therefore produce stack traces on failed assertions.
- libc is available for this target even when cross compiling.
Tier 2 Support §
- There may be some standard library implementations, but many abstractions will give an "Unsupported OS" compile error. One can link with libc or other libraries to fill in the gaps in the standard library.
- These targets are known to work, but are not automatically tested, so there are occasional regressions.
- Some tests may be disabled for these targets as we work toward Tier 1 support.
Tier 3 Support §
- The standard library has little to no knowledge of the existence of this target.
- Because Zig is based on LLVM, it has the capability to build for these targets, and LLVM has the target enabled by default.
- These targets are not frequently tested; one will likely need to contribute to Zig in order to build for these targets.
- The Zig compiler might need to be updated with a few things such as
- what sizes are the C integer types
- C ABI calling convention for this target
- bootstrap code and default panic handler
zig targetsis guaranteed to include this target.
Tier 4 Support §
- Support for these targets is entirely experimental.
- LLVM may have the target as an experimental target, which means that you
need to use Zig-provided binaries for the target to be available, or
build LLVM from source with special configure flags.
zig targetswill display the target if it is available. - This target may be considered deprecated by an official party, such as macosx/i386 in which case this target will remain forever stuck in Tier 4.
- This target may only support
--emit asmand cannot emit object files.
RISC-V Support §
This release updates to LLVM 9, musl 1.1.23 with patches, and glibc 2.30. This plus updates to the Standard Library means that Zig's (64-bit) RISC-V support has gone from Tier 4 Support to Tier 3 Support in this release.
- daurnimator updated riscv64 syscalls in the std lib.
- LemonBoy updated thread-local storage startup code to take into account DTP bias according to the RISCV ABI. He also corrected some of the bits in the std lib. Thanks to this the compiler-rt tests are passing for RISC-V.
RISC-V is a very flexible target, with features such as atomics, and even integer multiplication
being optional. Since Zig does not yet have
ability to specify target CPU features,
the default set of cross-compilation features are "+a,+c,+d,+f,+m,+relax",
which matches Clang.
The RISC-V target does not yet pass Zig's test suite:
- LLVM ERROR: Named registers not implemented for this target
- LLVM unable to perform f16 widening
- LLD silent failure linking behavior tests
The next step for all these issues is to create test case reductions and then file upstream bug reports.
However, I did work with Rich Felker to get musl building with Clang for the RISC-V target, which means that we can do this:
hello.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello world\n");
return 0;
}
$ zig build-exe --c-source hello.c -lc -target riscv64-linux-musl
$ qemu-riscv64 ./hello
Hello world
Zig 0.5.0 carries a few patches to musl which makes this work. A new musl release is expected soon which contains these patches.
To be clear - the above simple example also works in Zig - it's just that all the language features
such as f16 are not working, and there is no automated Test Coverage
for this target.
hello.zig
const std = @import("std");
pub fn main() void {
std.debug.warn("Hello from zig\n");
}$ zig build-exe hello.zig -target riscv64-linux
$ qemu-riscv64 ./hello
Hello from zig
Although glibc 2.30 gained RISC-V support, Zig is not able to build glibc for this target yet. See #3340 for more details. Looks like it could be as simple as importing a couple more .h files from the glibc source tree.
64-bit ARM Support §
LemonBoy worked on Standard Library support for aarch64 during this release cycle:
- Several improvements to Thread Local Storage.
- Fixed the definition of
epoll_*struct on non x86_64 arches. - Fixed some syscalls and flag definitions.
After this work, and improvements made to Test Coverage, the following targets are now covered by the Zig test suite:
- aarch64v8_5a-linux-none (no libc)
- aarch64v8_5a-linux-musl (building musl 1.1.23 from source, statically linking against it, and using it for OS APIs)
- aarch64v8_5a-linux-gnu (building glibc 2.30 from source, dynamically linking against it, and using it for OS APIs)
This test coverage led to the following bug fixes:
- LemonBoy fixed a test on ARM due to the use of
undefinedptr. - Fixed Linux stat struct on aarch64. The ABI was incorrect.
- Fixed glibc 2.30 builds on aarch64-linux-gnu. There was a missing include path in the compilation line, leading to incorrect fstat ABI. #3291
However, due to LLVM miscompiling trivial switch for AArch64, some failing tests are disabled, which means 64-bit ARM remains a Tier 2 Support target. The good news is we filed an LLVM bug report, and it has already been solved in LLVM trunk, scheduled to be included in LLVM 9.0.1.
After that, the only remaining issues standing in the way of Tier 1 Support for ARM 64-bit (aarch64) Linux are:
- When targeting AArch64, unable to widen cast f16 to f128
- fn return struct - aarch64 stack alignment exception
- @newStackCall() generates invalid machine code
- More C ABI support for this target
- Add Test Coverage for Debug Info and Stack Traces for this target.
- Come up with a way for the Continuous Integration service to create 64-bit ARM binaries for the download page.
arm64-test.zig
const builtin = @import("builtin");
const std = @import("std");
const assert = std.debug.assert;
test "cross compiled unit test" {
assert(builtin.arch == .aarch64);
}
$ zig test arm64-test.zig -target aarch64v8-linux --test-cmd qemu-aarch64 --test-cmd-bin
1/1 test "cross compiled unit test"...OK
All tests passed.
32-bit ARM Support §
Thanks to compiler-rt improvements by vegecode and LemonBoy, Zig's 32-bit ARM support is much stronger in version 0.5.0.
Alongside these efforts, LemonBoy improved the Standard Library by making
I/O offsets and sizes u64 instead of usize,
decoupling the concepts of address-space size and file size. This solved many compile errors
when trying to target 32-bit ARM, as well as any other 32-bit architecture. #637
He also made several improvements to Thread Local Storage for 32-bit ARM.
Robin Voetter joined the Zig community during this release cycle, and hammered away at the Standard Library:
- Added arm32 linux bits definitions and syscall conventions.
- TLS initialization, clone definition and _start functionality.
- Made
std.os.linux.mmapuseSYS_mmap2if it exists. - Used
sys_*stat*64instead ofsys_*stat*where appropriate. - Fixed overflow when calculating atime, ctime and mtime on File.stat().
- Fixed compilation error casting getEndPos to usize.
- Fixed preadv, preadv2, pwritev and pwritev2.
- Fixed seteuid and setegid.
- Replaced legacy 16-bit syscalls with 32-bit versions when appropriate.
- Added missing clobbers on arm-eabi and arm64 syscall conventions.
After all these improvements, 32-bit ARM support is leveled-up to Tier 2 Support. Along with improvements made to Test Coverage, the following targets are now covered by the Zig test suite:
- armv8_5a-linux-none (no libc)
- armv8_5a-linux-musl (building musl 1.1.23 from source, statically linking against it, and using it for OS APIs)
- armv8_5a-linux-gnu test coverage is not passing yet
arm32-test.zig
const builtin = @import("builtin");
const std = @import("std");
const assert = std.debug.assert;
test "cross compiled unit test" {
assert(builtin.arch == .arm);
}
$ zig test arm32-test.zig -target armv8-linux --test-cmd qemu-arm --test-cmd-bin
1/1 test "cross compiled unit test"...OK
All tests passed.
After updating to musl 1.1.23, Zig's clone on arm32 is updated to
latest musl implementation.
Remaining issues to solve in order to achieve Tier 1 Support for ARM 32-bit Linux:
- armv7 __aeabi_uidivmod produces undefined instruction exception
- std lib tests failing on arm 32-bit
- unable to build glibc for target armv8-linux-gnueabihf
- Constant packed struct instantiations can generate incorrect unaligned loads on armv4t/armv5
- Add Test Coverage for Debug Info and Stack Traces for this target.
- Come up with a way for the Continuous Integration service to create 32-bit ARM binaries for the download page.
MSYS2 Support §
Although Zig does not officially support MSYS2 as a host target, emekoi has dutifully maintained unofficial support. Thanks to emekoi's efforts, one can build and run the stage1 C++ compiler of Zig in an MSYS2 environment.
Note: sometimes "MinGW" is used as a shorthand to mean "MSYS2". However, it is not to be confused with mingw-w64, or with the unrelated project, MinGW. MinGW-w64 is a fork of MinGW, which adds support for more architectures (such as 64-bit Windows) and more system APIs. When someone says "MinGW", it's almost certain they either mean "MSYS2" (which is based on MinGW-w64) or "MinGW-w64" instead.
Here is the list of things emekoi did to maintain unofficial support for MSYS2:
- fixed linking of system libraries on msys2
- added code for linking libc on msys2
- made lld flags on windows consistent
- renamed add_gnu_link_args
- fixed stack protector issues
- added static_crt_dir to libc file
- fixed visibility of zig_libc_cc_print_file_name
- supress warnings for format strings on msys64
- make string literal const on windows
- increase stack size for msys2
- fixed compilation on mingw-w64
- fixed static linking on mingw-w64
- fixed size of ZigClangAPValue on mingw-w64
- improved CMake file for msys2
- fixed backtraces when linking libc on msys2
- avoid passing -static to msvc when static linking
FreeBSD Support §
stratact made the following changes:
- Added missing
<stdint.h>include foruint8_ttype declaration. - Added needed FreeBSD check to link to libpthread.
- Added missing C
dl_iterate_phdrfunction for FreeBSD.
This combined with disabling some of the failing standard library tests for FreeBSD, stratact was able to enable more Test Coverage for FreeBSD. Now the Continuous Integration server runs 7 additional kinds of tests from the test suite, instead of only the behavior tests.
stratact reports all tests passing locally, however, we have run into memory limits of SourceHut, which is the service used to run FreeBSD tests. Drew DeVault has understandably denied our request for more RAM, and so we are left with disabled test coverage until Zig can finish self-hosting, or improve the memory usage of the C++ stage1 compiler.
The set of remaining issues until Tier 1 FreeBSD Support for x86_64 is now:
- The event tests hang on FreeBSD. Event-based code is still pending a rewrite now that Async Functions have been redesigned.
- FreeBSD CI tests disabled due to using more memory than sr.ht provides
- add FreeBSD libc as a cross compilation target. This is the task to do for FreeBSD what Zig already does for musl, glibc, and mingw-w64.
WebAssembly Support §
During the 0.5.0 release cycle, Shritesh Bhattarai joined the Zig community and made significant contributions to Zig's WebAssembly and WASI (Web Assembly System Interface) support.
He got compiler-rt working and tweaked the target settings such as:
- disabling error return traces
- forcing single-threaded mode
- making the executable file extension ".wasm"
Thanks to this as well as Shritesh adding basic standard library support for the WASI target, as well as improving the linker settings that Zig uses, WebAssembly and WASI are now Tier 2 Support targets!
As a demo, Shritesh created zigfmt-web, which
is a web page that will run zig fmt on a block of code, using the same
implementation as official zig fmt.
Shritesh created a basic allocator intended to be used on the WebAssembly target,
std.heap.wasm_allocator. This uses the WebAssembly intrinsics to request memory
from the host, and is not capable of freeing memory. The standard library does not yet have
an allocator for WebAssembly that can reclaim freed memory. See
zee_alloc for a community project
attempting to solve this use case.
Shritesh also created a demo of Zig interacting with the DOM via JS (source).
Other miscellaneous improvements to WebAssembly:
- update
std.os.page_sizefor WebAssembly - Benjamin Feng switched wasm export-all to only values marked exports, so that unused symbols get properly garbage collected, resulting in smaller wasm binaries.
- LemonBoy implemented compiler-rt builtins for WebAssembly.
- Benjamin Feng updated the library prefix to be empty for WebAssembly rather than "lib".
Zig now provides a Freestanding libc, which is available when linking libc for the WebAssembly target. It is not yet fully complete, but you can get a sense of the use case for it with this demo project: lua-in-the-browser
This use case led to several improvements to Zig's WebAssembly support:
build-exeis for executables which have amain().build-libis for building libraries of functions to use from, for example, a web browser environment.- For now pass
--export-allfor libraries when there are any C objects because we have no way to detect the list of exports when compiling C code. - Zig no longer passes
--no-entryto the linker for executables. If you want--no-entrythen usebuild-lib. - Made the "musl" ABI the default ABI for wasm32-freestanding.
build-exedoes include the startup code that supplies_startfor thewasm32-freestandingtarget. Previously this did not occur because of logic excluding "freestanding".build-libforwasm32-freestandingtarget gets linked by LLD. To avoid infinite recursion, compiler-rt and Freestanding libc are built as objects rather than libraries.- "lib" prefix and ".wasm" extension instead of ".a". Rather than
build-lib foo.zigproducing "libfoo.a", now it produces "foo.wasm". - Freestanding libc only provides
_startsymbol for wasm when linking libc.
Zig is particularly well suited to creating reasonably small & fast WebAssembly binaries. Here are some demos of WebAssembly projects from Zig community members:
- Oxid - an arcade-style game where you fight waves of monsters in a fixed-screen maze.
- FUN✜DUDE - gameboy emulator.
- Tetris - WebGL tetris.
UEFI Support §
Nick Erdmann has been reading the UEFI specification and improving Zig support for this target.
Zig's Standard Library now integrates more cleanly with UEFI, and other things now "just work" such as PDB files and 0x0 addresses.
Many of the UEFI protocol definitions are now available in std.os.uefi.protocols.
Nick has clean and well-organized demo projects which serve as resources to help others learn how to do UEFI programming:
- uefi-examples - tutorials
- uefi-paint - UEFI-bootable touch paint app
- uefi-freetype - changing the system BIOS to use Comic Sans🤡
iOS Support §
Matthew Iannucci added initial support for iOS targets (#2237).
However iOS remains a Tier 3 Support target. There are no known active Zig projects targeting iOS.
MIPS Support §
LemonBoy implemented Thread Local Storage for architectures that have thread pointer offsets, such as mipsel. He updated the Standard Library with the Linux system bits for the mipsel architecture, and worked with musl upstream to get it patched enough to be able to successfully build with Clang for this target. Zig carries this patch in 0.5.0.
After these changes, MIPS now has Tier 2 Support! LemonBoy reports running a Zig binary on his router:
18:59 <TheLemonMan> just got a Zig binary running on my mips32 router, yay
These targets are now covered by the Zig test suite:
- mipsel-linux-none (no libc)
- mipsel-linux-musl (building musl 1.1.23 from source, statically linking against it, and using it for OS APIs)
- mipsel-linux-gnu remains to be investigated
Aside from investigating mipsel-linux-gnu, the only remaining issues standing in the way of
Tier 1 Support for MIPS Little-Endian Linux (mipsel-linux)
are:
- LLVM miscompiles some vector instructions - we still need to file the upstream bug report
- Add Test Coverage for Debug Info and Stack Traces for this target.
- Come up with a way for the Continuous Integration service to create binaries for the download page.
mips-test.zig
const builtin = @import("builtin");
const std = @import("std");
const assert = std.debug.assert;
test "cross compiled unit test" {
assert(builtin.arch == .mipsel);
}
$ zig test mips-test.zig -target mipsel-linux --test-cmd qemu-mipsel --test-cmd-bin
1/1 test "cross compiled unit test"...OK
All tests passed.
Android Support §
meme joined the Zig community and contributed improvements
to the target aarch64-linux-android. Thanks to their efforts, Zig now has
Tier 2 Support for Android.
Here's an example of building an Android executable with Zig:
hello_android.zig
const std = @import("std");
pub fn main() void {
std.debug.warn("Hello, Android!");
}
$ zig build-exe hello_android.zig -target aarch64-linux-android
$ file ./hello_android
./hello_android: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, with debug_info, not stripped
In this example, there is no libc dependency. However, Zig does know how to integrate
with Android's libc. The first step is to create a libc text file describing where various paths
are. One can obtain a template for this file by executing zig libc. In this example,
I've taken the template and populated it based on the path to the Android NDK in my downloads
folder:
android_libc.txt
# The directory that contains `stdlib.h`.
# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
include_dir=/home/andy/Downloads/android-ndk-r20/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include
# The system-specific include directory. May be the same as `include_dir`.
# On Windows it's the directory that includes `vcruntime.h`.
# On POSIX it's the directory that includes `sys/errno.h`.
sys_include_dir=/home/andy/Downloads/android-ndk-r20/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include
# The directory that contains `crt1.o` or `crt2.o`.
# On POSIX, can be found with `cc -print-file-name=crt1.o`.
# Not needed when targeting MacOS.
crt_dir=/home/andy/Downloads/android-ndk-r20/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/21
# The directory that contains `crtbegin.o`.
# On POSIX, can be found with `cc -print-file-name=crtbegin.o`.
# Not needed when targeting MacOS.
static_crt_dir=/home/andy/Downloads/android-ndk-r20/toolchains/llvm/prebuilt/linux-x86_64/lib/gcc/aarch64-linux-android/4.9.x
# The directory that contains `vcruntime.lib`.
# Only needed when targeting MSVC on Windows.
msvc_lib_dir=
# The directory that contains `kernel32.lib`.
# Only needed when targeting MSVC on Windows.
kernel32_lib_dir=
hello_libc.zig
const std = @import("std");
extern fn printf(msg: [*]const u8, ...) c_int;
pub fn main() void {
_ = printf(c"hello android libc\n");
}
$ zig build-exe hello_libc.zig -target aarch64-linux-android -lc --libc android_libc.txt
$ file ./hello_libc
./hello_libc: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /system/bin/linker64, with debug_info, not stripped
Language Changes §
- Slice types no longer have field access.
Instead, use
@typeInfo(Slice).Pointer.child. - Enum literals now implicit cast to tagged unions.
- Allow implicit casting optional pointer to optional
c_voidpointer. - Sam Tebbs added implicit cast for
*[N]Tto[*c]T. - Stevie Hryciw made
anyerrorno longer a keyword. #2835 - @cmpxchgStrong and @cmpxchgWeak now support 128-bit integers on x86_64.
comptime_intnow implicit casts tocomptime_float.- @typeOf now guarantees no runtime side effects.
- Timon Kruiper added implicit casting from enum literal to optional enum and implicit casting to payload of error union.
- LemonBoy added the
noinlinekeyword for function declarations. - Comparison between union tag and enum literal is now allowed thanks to LemonBoy. #2810
- LemonBoy made Single-element
enumdefault tou0. However,comptime_intis still allowed as an explicit enum tag type. #2997
usingnamespace §
usingnamespace is a top level declaration that imports all the public declarations of
the operand, which must be a struct, union, or
enum, into the current scope:
usingnamespace.zig
usingnamespace @import("std");
test "using std namespace" {
debug.assert(true);
}$ zig test usingnamespace.zig
1/1 test "using std namespace"...OK
All tests passed.
Instead of the above pattern, it is generally recommended to explicitly alias individual declarations.
However, usingnamespace has an important use case when organizing the public
API of a file or package. For example, one might have c.zig with all of the
C imports:
pub usingnamespace @cImport({
@cInclude("epoxy/gl.h");
@cInclude("GLFW/glfw3.h");
@cDefine("STBI_ONLY_PNG", "");
@cDefine("STBI_NO_STDIO", "");
@cInclude("stb_image.h");
});
The above example demonstrates using pub to qualify the
usingnamespace additionally makes the imported declarations
pub. This can be used to forward declarations, giving precise control
over what declarations a given file exposes.
In Zig 0.4.0, this feature existed as use, but it only worked at the top-level
scope, and only for structs. The feature was also considered unstable.
Thank you LemonBoy for fixing usingnamespace outside the top-level scope, and
making it work with arbitrary structs.
In Zig 0.5.0, both use and usingnamespace are accepted, and
zig fmt automatically converts to the canonical syntax. The next release of Zig after this
one will remove the old syntax.
This feature is now stable and planned to be included in the language specification.
External Thread Local Variables §
Zig now always respects threadlocal for variables with external linkage.
Previously, if you had, for example:
extern "c" threadlocal var errno: c_int;
This would turn errno into a normal variable for --single-threaded
builds. However for variables with external linkage, there is an ABI
to uphold.
This is needed to make errno work for DragonFly BSD. See #2381.
@hasField and @hasDecl §
@hasField(comptime Container: type, comptime name: []const u8) bool
@hasDecl(comptime Container: type, comptime name: []const u8) bool
The new builtin function @hasField returns whether the field name of a struct, union, or enum exists. The result is a compile time boolean. It does not include functions, variables, or constants.
The new builtin function @hasDecl returns whether or not a struct, enum, or union has a declaration.
has_builtins.zig
const std = @import("std");
const assert = std.debug.assert;
const Foo = struct {
nope: i32,
pub var blah = "xxx";
const hi = 1;
};
test "@hasDecl and @hasField" {
assert(@hasDecl(Foo, "blah"));
// Even though `hi` is private, @hasDecl returns true because this test is
// in the same file scope as Foo. It would return false if Foo was declared
// in a different file.
assert(@hasDecl(Foo, "hi"));
// @hasDecl is for declarations; not fields.
assert(!@hasDecl(Foo, "nope"));
assert(!@hasDecl(Foo, "nope1234"));
assert(@hasField(Foo, "nope"));
}$ zig test has_builtins.zig
1/1 test "@hasDecl and @hasField"...OK
All tests passed.
Thanks to Shawn Landden for initial implementation and documentation of @hasField.
C Pointers Support Optional Syntax §
When translating C code, Zig does not know whether pointer types
should be translated to * or [*] pointers. Instead, they
are translated to C Pointers.
As the documentation notes, this type is to be avoided whenever possible. The only valid reason for using a C pointer is in auto-generated code from translating C code.
Interfacing with C pointer types happens due to direct interop with translated .h files. It's always
a future possibility to rewrite the .h file in .zig to gain better type-safety. Previously,
optional syntax, such as if, orelse, null,
and .? did not work for C pointers. This would cause compile errors if
the type signature of the external function prototypes were improved to have the real pointer types
rather than C pointers.
Now, this syntax works, and so there is no penalty for starting out with auto-translated headers, and then later "upgrading" to better typed bindings.
getenv.zig
const getenv = @cImport(@cInclude("stdlib.h")).getenv;
// note: this is just a demo of C pointers with optional syntax.
// std.process has better API for getenv.
test "C pointers with optional syntax" {
const ptr1 = getenv(c"HOME").?; // don't do this 💥
const ptr2 = getenv(c"HOME") orelse return error.Homeless; // OK
if (getenv(c"HOME")) |ptr3| {
// also OK
}
const ptr4 = getenv(c"HOME");
if (ptr4 == null) {
// also works
}
}$ zig test getenv.zig -lc
1/1 test "C pointers with optional syntax"...OK
All tests passed.
The auto-translated getenv prototype looks like this:
pub extern fn getenv(__name: [*c]const u8) [*c]u8;
If we were to improve this prototype with correct pointer types, the test will still pass:
getenv2.zig
pub extern fn getenv(name: [*]const u8) ?[*]u8;
// note: this is just a demo of C pointers with optional syntax.
// std.process has better API for getenv.
test "C pointers with optional syntax" {
const ptr1 = getenv(c"HOME").?; // don't do this 💥
const ptr2 = getenv(c"HOME") orelse return error.Homeless; // OK
if (getenv(c"HOME")) |ptr3| {
// also OK
}
const ptr4 = getenv(c"HOME");
if (ptr4 == null) {
// also works
}
}$ zig test getenv2.zig -lc
1/1 test "C pointers with optional syntax"...OK
All tests passed.
Switching on Error Sets §
Using switch on an
error set now provides a way
to capture an error value with a subset type:
switch_err_set_1.zig
const std = @import("std");
const os = std.os;
const Error = error{
AccessDenied,
FileTooBig,
IsDir,
SymLinkLoop,
ProcessFdQuotaExceeded,
NameTooLong,
SystemFdQuotaExceeded,
NoDevice,
FileNotFound,
SystemResources,
NoSpaceLeft,
NotDir,
PathAlreadyExists,
DeviceBusy,
InputOutput,
OperationAborted,
BrokenPipe,
Unexpected,
};
pub fn main() Error!void {
// open a normal, blocking file descriptor.
const fd = try os.open("/dev/urandom", os.O_RDONLY, 0);
defer os.close(fd);
// we did *not* use O_NONBLOCK, so the OS will not give us
// EWOULDBLOCK.
var buf: [100]u8 = undefined;
const nbytes = try readBlocking(fd, &buf);
}
/// Asserts that fd was opened in a blocking fashion.
fn readBlocking(fd: os.fd_t, buffer: []u8) !usize {
return std.os.read(fd, buffer) catch |err| switch (err) {
error.WouldBlock => unreachable, // Remove this to observe compile error
else => |e| return e,
};
}$ zig build-exe switch_err_set_1.zig
$ ./switch_err_set_1
Here you can see that the program compiled just fine, even though error.WouldBlock
is not found in the error set. This is because the function readBlocking
switched on the error set, and handled the error.WouldBlock case. This means
the error set type of value captured by the else does not include the value
error.WouldBlock.
In addition to this, Zig allows capturing the payload from multiple error set values:
switch_err_set_2.zig
const std = @import("std");
const os = std.os;
const Error = error{
AccessDenied,
FileTooBig,
IsDir,
SymLinkLoop,
ProcessFdQuotaExceeded,
NameTooLong,
SystemFdQuotaExceeded,
NoDevice,
FileNotFound,
SystemResources,
NoSpaceLeft,
NotDir,
PathAlreadyExists,
DeviceBusy,
OperationAborted,
BrokenPipe,
Unexpected,
};
pub fn main() Error!void {
// open a normal, blocking file descriptor.
const fd = try os.open("/dev/urandom", os.O_RDONLY, 0);
defer os.close(fd);
// we did *not* use O_NONBLOCK, so the OS will not give us
// EWOULDBLOCK.
var buf: [100]u8 = undefined;
const nbytes = try readBlocking(fd, &buf);
}
/// Asserts that fd was opened in a blocking fashion.
fn readBlocking(fd: os.fd_t, buffer: []u8) !usize {
return std.os.read(fd, buffer) catch |err| switch (err) {
error.WouldBlock, error.InputOutput => |e| {
std.debug.panic("unexpected: {}\n", e);
},
else => |e| return e,
};
}$ zig build-exe switch_err_set_2.zig
$ ./switch_err_set_2
In this example, error.InputOutput was lifted out of Error
since it is handled inside readBlocking. The e capture value
has type error{WouldBlock,InputOutput}.
Bit Manipulation Builtin Functions §
Shawn Landden improved the consistency of the names and parameters of bit manipulation intrinsics.
- Rename
@bitreverseto @bitReverse to adhere to naming conventions. - Rename
@bswapto @byteSwap. - @clz, @ctz, @popCount, @byteSwap, and @bitReverse now have an explicit type parameter.
@bitReverse(comptime T: type, integer: T) T
@byteSwap(comptime T: type, operand: T) T
@clz(comptime T: type, integer: T)
@ctz(comptime T: type, integer: T)
@popCount(comptime T: type, integer: T)
Undeclared Identifiers in Compile-Time Dead Branches §
Zig no longer validates whether identifiers exist in dead comptime branches:
dead_comptime_branch.zig
test "dead comptime branch" {
if (false) {
does_not_exist = thisFunctionAlsoDoesNotExist();
}
}$ zig test dead_comptime_branch.zig
1/1 test "dead comptime branch"...OK
All tests passed.
This is counter-intuitive, but consider that the set of available identifiers may depend on
comptime parameters, such as the target OS:
const builtin = @import("builtin");
usingnamespace switch (builtin.os) {
.windows => @import("windows_stuff.zig"),
else => @import("posix_stuff.zig"),
};
test "example" {
if (builtin.os == .windows) {
ExitProcess(0);
} else {
exit(0);
}
}
In practice, this has resulted in various code cleanups throughout the standard library.
Zig's lazy analysis, while convenient, surfaces the inherent problems of conditional compilation. See the related proposal: "multibuilds" - a plan to harmonize conditional compilation with compile errors, documentation, and IDEs
Default Struct Field Values §
Each struct field may now have an expression indicating the default field value. Such expressions
are executed at comptime, and allow the field to be omitted in a struct
literal expression:
default_fields.zig
const Foo = struct {
a: i32 = 1234,
b: i32,
};
test "default struct field values" {
const x = Foo{
.b = 5,
};
if (x.a + x.b != 1239) {
@compileError("it's even comptime known!");
}
}$ zig test default_fields.zig
1/1 test "default struct field values"...OK
All tests passed.
Array Literal Syntax §
The array literal syntax has changed, when inferring the size.
Old syntax:
[]i32{1, 2, 3}
New syntax:
[_]i32{1, 2, 3}
The previous syntax used to look too much like instantiating a slice. This caused all kinds of confusion. Now it's pretty clear that the type is an array.
@import("root") §
The Root Source File
(in the case of build-exe, the file with pub fn main)
is now available to import anywhere, using @import("root").
Combined with @hasDecl,
this allows library code to support global configuration settings based on declarations in the
root source file.
The Standard Library takes advantage of this for several use cases. One example is
the Default Segfault Handler. It works like this (from std.debug):
const root = @import("root");
/// Whether or not the current target can print useful debug information when a segfault occurs.
pub const have_segfault_handling_support = builtin.os == .windows or
(builtin.arch == builtin.Arch.x86_64 and builtin.os == .linux);
pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
root.enable_segfault_handler
else
runtime_safety and have_segfault_handling_support;
pub fn maybeEnableSegfaultHandler() void {
if (enable_segfault_handler) {
std.debug.attachSegfaultHandler();
}
}
And then Zig's startup code calls std.debug.maybeEnableSegfaultHandler() just before
calling main().
Another place this is used in the standard library is to decide a global "I/O mode", which is related to Async Functions.
This feature has the capability to be abused, and should be used with care. Any root source file declarations that can affect a library's behavior should be well-documented. When Zig gains documentation generation, the auto-generated docs will have the capability to enumerate all the places that depend on a root source file declaration.
Thank you emekoi for the initial implementation of this.
@mulAdd §
Zig now has @mulAdd, otherwise known as "fused-multiply-add".
@mulAdd(comptime T: type, a: T, b: T, c: T) T
Performs (a * b) + c, except only rounds once, and is thus more accurate.
Additionally, some targets have a hardware instruction for this, making it potentially faster than
a userland implementation.
mul_add.zig
const std = @import("std");
test "@mulAdd" {
// In this example we use numbers small enough to avoid rounding errors that would occur
// without @mulAdd.
std.testing.expect(@mulAdd(f32, 2.0, 3.0, 4.0) == (2.0 * 3.0) + 4.0);
}$ zig test mul_add.zig
1/1 test "@mulAdd"...OK
All tests passed.
Currently this instruction only works for floating point types, as well as
vectors of floating point types. However, there is
an open proposal
to make this work for any types that support * and +
operators, such as integers. The proposal also suggests to remove the explicit type parameter requirement.
Thank you Shawn Landden for the initial implementation of this.
Builtins for Math Functions §
Shawn Landden added:
@sin(comptime T: type, value: T) T
@cos(comptime T: type, value: T) T
@exp(comptime T: type, value: T) T
@exp2(comptime T: type, value: T) T
@ln(comptime T: type, value: T) T
@log2(comptime T: type, value: T) T
@log10(comptime T: type, value: T) T
@fabs(comptime T: type, value: T) T
@floor(comptime T: type, value: T) T
@ceil(comptime T: type, value: T) T
@trunc(comptime T: type, value: T) T
@round(comptime T: type, value: T) T
These are builtin functions because some architectures have hardware instructions for these. Furthermore, because these functions are well-defined, the optimizer may sometimes be able to convert calls to these builtins into better forms.
It's planned for Zig to provide libmvec in the future, and these functions will become
SIMD-capable.
Result Location Semantics §
What I'm calling Result Location Semantics was a large branch of Zig that fundamentally changed the way that expressions are semantically analyzed. This was the third attempt, which finally succeeded. I abandoned the first attempt after 1 week. The second attempt, which took place during the 0.4.0 release cycle, lasted 2 months, but again was regretfully abandoned. However, there were significant parts of the second attempt that landed in the eventual implementation.
During my work on this branch, the Zig community stepped up and continued to improve master branch all the while. You can observe this by seeing how many names are mentioned in these release notes. I am proud and grateful of the Zig community for this.
Although the implementation was difficult, the user-facing differences of Result Location Semantics are nearly impossible to detect. The main purpose was to pave the way for the redesign of Async Functions.
The main thing that this change does is semantically guarantee that no copying happens in expressions. As an example:
result_loc.zig
const std = @import("std");
const Object = struct {
tag: i32,
pt: [2]Point,
};
const Point = struct {
x: i32,
y: i32,
};
test "result location semantics" {
const result = if (condition()) foo(10) else bar();
std.testing.expect(result.tag == 10);
std.testing.expect(result.pt[0].x == 69);
std.testing.expect(result.pt[1].y == 420);
}
fn condition() bool {
return true;
}
fn foo(arg: i32) Object {
return baz(arg);
}
fn bar() Object {
return Object{
.tag = 1,
.pt = undefined,
};
}
fn baz(arg: i32) Object {
return Object{
.tag = arg,
.pt = [_]Point{ nice(), blazet() },
};
}
fn nice() Point {
return Point{
.x = 69,
.y = 69,
};
}
fn blazet() Point {
return Point{
.x = 420,
.y = 420,
};
}$ zig test result_loc.zig
1/1 test "result location semantics"...OK
All tests passed.
The important thing to note here is that the functions nice() and
blazet() write directly to result in the
main test function. There are no intermediate copies, and this is semantically guaranteed
by the language.
With Zig's current semantics, it is actually not possible to observe the difference between 0.4.0 and 0.5.0 (except with async function calls). However, with future proposals to the language it would matter a great deal:
- result location: ability to refer to the return result location before the `return` statement
- result locations: unwrap optional and error unions so that the payload can be non-copied
The point here is that initialization functions would be able to set up pointer references relative to the return value, and have the value be guaranteed to be valid.
Before moving on to the next section I want to say a huge thank you to Michael Dusan. This branch was a dizzying amount of effort, and towards the end of it, Michael started contributing. He created test case reductions and even solved some of the regressions, such as vector to array conversion not being aligned correctly. This was both unexpected and helpful. It made a serious difference in getting me through to the end of the branch, so that we could merge it into master.
The other thing that came out of this branch was preferring the "result type" to Peer Type Resolution:
result_loc_peer.zig
const std = @import("std");
const expect = std.testing.expect;
test "result location type resolution" {
var f = false;
var x: i32 = 0;
x = if (f) 1 else 2;
expect(x == 2);
}$ zig test result_loc_peer.zig
1/1 test "result location type resolution"...OK
All tests passed.
Additionally:
- LemonBoy fixed result loc unwrapping with optional in error union. #2899
@unionInit §
@unionInit(comptime Union: type, comptime active_field_name: []const u8, init_expr) Union
This is the same thing as union initialization syntax, except that the field name is a comptime-known value rather than an identifier token.
@unionInit forwards its result location to init_expr.
Thank you Robert Scott for the initial implementation of @unionInit.
Unicode Escapes §
Zig's unicode escape syntax is changed to match most other popular programming languages.
Old syntax:
"\U01f4a9"
New syntax:
"\u{1f4a9}"
This matches JavaScript (since ES6), Lua (since 5.3), Swift (who swapped from our previous syntax!), and Rust.
Thank you daurnimator for doing the research on other languages, and Shawn Landden for the implementation.
It is
planned to additionally allow unicode escapes in character literals,
since character literals have type comptime_int. That accepted proposal is marked
"Contributor Friendly" because it is limited in scope and/or knowledge of Zig internals.
Async Functions §
async functions have been completely reworked in Zig 0.5.0. Previously,
I was calling these "stackless coroutines". However I'm now avoiding the word "coroutine" since
it means different things to different people, and instead using the phrase "async functions".
In Zig 0.4.0, all async functions were generic across an allocator type, all async functions took an allocator parameter, and calling an async function could fail. Additionally, async functions were required to be annotated as such.
In Zig 0.5.0, calling an async function can no longer fail. The async function frame is provided
by the caller via Result Location Semantics, and can be in the caller's stack frame. Async
functions are no longer generic, and do not require the async keyword.
Zig infers that a function is async when it observes that the function contains
a suspension point. Async functions can be called the same as normal functions. A
function call of an async function is a suspend point.
When a regular function is called, a frame is pushed to the stack, the function runs until it reaches a return statement, and then the frame is popped from the stack. At the callsite, the following code does not run until the function returns.
An async function is a function whose callsite is split into an async initiation,
followed by an await completion. Its frame is
provided explicitly by the caller, and it can be suspended and resumed any number of times.
Here's a simple example of an async function:
async_fn.zig
const std = @import("std");
var frame: anyframe = undefined;
pub fn main() void {
std.debug.warn("begin main\n");
_ = async func();
std.debug.warn("resume func\n");
resume frame;
std.debug.warn("end main\n");
}
fn func() void {
std.debug.warn("begin func\n");
frame = @frame();
suspend;
std.debug.warn("end func\n");
}$ zig build-exe async_fn.zig
$ ./async_fn
begin main
begin func
resume func
end func
end main
Here we have a seam between non-async (main) and async (func) code. A more typical usage of this feature:
typical_async_await.zig
const std = @import("std");
const expect = std.testing.expect;
// Try toggling these
const simulate_fail_download = false;
const simulate_fail_file = false;
const suspend_download = true;
const suspend_file = true;
pub fn main() void {
_ = async amainWrap();
// This simulates an event loop
if (suspend_file) {
resume global_file_frame;
}
if (suspend_download) {
resume global_download_frame;
}
}
fn amainWrap() void {
if (amain()) |_| {
expect(!simulate_fail_download);
expect(!simulate_fail_file);
} else |e| switch (e) {
error.NoResponse => expect(simulate_fail_download),
error.FileNotFound => expect(simulate_fail_file),
else => @panic("test failure"),
}
}
fn amain() !void {
const allocator = std.heap.direct_allocator;
var download_frame = async fetchUrl(allocator, "https://example.com/");
var download_awaited = false;
errdefer if (!download_awaited) {
if (await download_frame) |x| allocator.free(x) else |_| {}
};
var file_frame = async readFile(allocator, "something.txt");
var file_awaited = false;
errdefer if (!file_awaited) {
if (await file_frame) |x| allocator.free(x) else |_| {}
};
download_awaited = true;
const download_text = try await download_frame;
defer allocator.free(download_text);
file_awaited = true;
const file_text = try await file_frame;
defer allocator.free(file_text);
expect(std.mem.eql(u8, "expected download text", download_text));
expect(std.mem.eql(u8, "expected file text", file_text));
std.debug.warn("OK!\n");
}
var global_download_frame: anyframe = undefined;
fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 {
const result = try std.mem.dupe(allocator, u8, "expected download text");
errdefer allocator.free(result);
if (suspend_download) {
suspend {
global_download_frame = @frame();
}
}
if (simulate_fail_download) return error.NoResponse;
std.debug.warn("fetchUrl returning\n");
return result;
}
var global_file_frame: anyframe = undefined;
fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 {
const result = try std.mem.dupe(allocator, u8, "expected file text");
errdefer allocator.free(result);
if (suspend_file) {
suspend {
global_file_frame = @frame();
}
}
if (simulate_fail_file) return error.FileNotFound;
std.debug.warn("readFile returning\n");
return result;
}$ zig build-exe typical_async_await.zig
$ ./typical_async_await
readFile returning
fetchUrl returning
OK!
The important thing to note here is that the async/await
mechanism did not bring in a dependency on the host OS, and it did not bring in a dependency on
an allocator.
Now watch what happens when we do this:
const suspend_download = false;
const suspend_file = false;
$ zig build-exe typical_async_await.zig
$ ./typical_async_await
fetchUrl returning
readFile returning
OK!
It's the same output, except in reversed order. With these modifications, there are no async
functions in the entire program! The expression
async fetchUrl(allocator, "https://example.com/") is evaluated as a normal,
blocking function, as is async readFile(allocator, "something.txt").
The awaits are no-ops.
The point here is that the amain function, which is the demo of typical async/await usage,
works in both an async context and blocking context. The programmer was able to express the inherent
parallelism of the logic, without resorting to
function coloring.
There is admittedly a bit of boilerplate in the example. Here's the tracking issue for that.
Now for the related Standard Library updates:
This introduces the concept of "IO mode" which is configurable by the
Root Source File (e.g. next to
pub fn main). Applications can put this in their root source file:
pub const io_mode = .evented;
This will populate std.io.mode to be std.io.Mode.evented.
When I/O mode is evented, std.os.read handles EAGAIN by suspending until the
file descriptor becomes available for reading. Although the std lib
event loop supports epoll, kqueue, and Windows I/O Completion Ports,
this integration with std.os.read currently only works on Linux.
This integration is currently only hooked up to std.os.read, and not,
for example, std.os.write, child processes, and timers. The fact that
we can do this and still have a working master branch is thanks to Zig's
lazy analysis, comptime, and inferred async. We can continue to make
incremental progress on async std lib features, enabling more and more
test cases and coverage.
In addition to std.io.mode there is std.io.is_async
which is equal to std.io.mode == .evented. In case I/O mode is async,
std.io.InStream notices this and the read function pointer becomes an async function
pointer rather than a blocking function pointer. Even in this case,
std.io.InStream can still be used as a blocking input stream.
Users of the API control whether it is blocking or async at runtime by whether
or not the read function suspends. In case of file descriptors, for
example, this might correspond to whether it was opened with O_NONBLOCK.
The noasync keyword makes a function call or await assert
that no suspension happens. This assertion has runtime safety enabled.
std.io.InStream, in the case of async I/O, uses by default a 1 MiB
frame size for calling the read function. If this is too large or too
small, the application can globally increase the frame size used by
declaring pub const stack_size_std_io_InStream = 1234; in their root
source file. This way, std.io.InStream will only be generated once,
avoiding bloat, and as long as this number is configured to be high
enough, everything works fine. Zig has runtime safety to detect when
@asyncCall is given too small of a buffer for the frame size.
This merge introduces -fstack-report which can help identify large async function frame sizes and explain what is making them so big.
-fstack-report outputs JSON format, which can then be viewed in a GUI that represents the tree structure. As an example, Firefox does a decent job of this.
One feature that is currently missing is detecting that the call stack upper bound is greater than the default for a given target, and passing this upper bound to the linker. As an example, if Zig detects that 20 MiB stack upper bound is needed - which would be quite reasonable - currently on Linux the application would only be given the default of 16 MiB.
There is so much to go over with this feature, and these release notes are already ridiculously long. I'm going to have to resort to listing out some things here, and rely on a future post to elaborate on these features.
@frameSize() usize
@frame() *@Frame(func)
@Frame(func: var) type
It is confirmed that async functions will solve safe recursion in Zig.
SIMD §
Zig's SIMD support in 0.5.0 is still far from complete, but significant progress has been made.
Shawn Landden has a branch of Zig with SIMD fairly complete, and has been maintaining this patchset, as I slowly upstream the commits one-by-one (with adjustments, fixups, etc). Shawn is giving a talk on his work at the October LLVM Dev Meeting: Using LLVM's portable SIMD with Zig
- Shawn Landden fixed array to vector and vector to array for many types, and
allowed vector of
bool. - Shawn Landden improved comparisons of vectors to return vectors instead of
bool. - Shawn Landden added @shuffle.
- Added @byteSwap support to vectors.
Thanks Shawn Landden for the
comptimeimplementation. - Shawn Landden added @splat.
See #903 for more details.
Alignment of Struct Fields §
In Zig 0.4.0 there was this ugly kludge in the C++ stage1 compiler:
// TODO If we have no type_entry for the field, we've already failed to
// compile the program correctly. This stage1 compiler needs a deeper
// reworking to make this correct, or we can ignore the problem
// and make sure it is fixed in stage2. This workaround is for when
// there is a false positive of a dependency loop, of alignment depending
// on itself. When this false positive happens we assume a pointer-aligned
// field, which is usually fine but could be incorrectly over-aligned or
// even under-aligned. See https://github.com/ziglang/zig/issues/1512
} else if (field->type_entry == nullptr) {
this_field_align = g->builtin_types.entry_usize->abi_align;
It was a mistake to ever let this kludge into the C++ stage1 compiler, and it was difficult to remove this kludge in 0.5.0. But it's gone now.
In Zig 0.5.0, the C++ stage1 compiler has the concept of "Lazy Values". This solved the problem
of false positive dependencies without a kludge such as this. It also enabled Zig programs
to explicitly specify struct field alignment:
field_align.zig
const std = @import("std");
const expect = std.testing.expect;
const Node = struct {
next: *Node,
massive_byte: u8 align(64),
};
test "struct field explicit alignment" {
var node: Node = undefined;
node.massive_byte = 100;
expect(node.massive_byte == 100);
comptime expect(@typeOf(&node.massive_byte) == *align(64) u8);
expect(@ptrToInt(&node.massive_byte) % 64 == 0);
}$ zig test field_align.zig
1/1 test "struct field explicit alignment"...OK
All tests passed.
In addition to this, "Lazy Values" solved the following bugs:
"Lazy Values" also paved the way for Standard Library integrations with Async Functions.
@Type §
@Type(comptime info: @import("builtin").TypeInfo) type
This function is the inverse of @typeInfo. It reifies type information
into a type.
It is available for the following types:
typenoreturnvoidbool- Integers- The maximum bit count for an integer type is
65535. - Floats
- Pointers
comptime_intcomptime_float@typeOf(undefined)@typeOf(null)
For these types it is a TODO in the compiler to implement:
- Array
- Optional
- ErrorUnion
- ErrorSet
- Enum
- Opaque
- FnFrame
- AnyFrame
- Vector
- EnumLiteral
For these types, @Type is not available.
There is an open proposal to allow unions and structs.
- union
- Functions
- BoundFn
- ArgTuple
- struct
Thank you to Jonathan Marler for the original implementation of @Type.
Variable Declarations as Methods §
Variable declarations can now be called as methods:
var_decl_methods.zig
const std = @import("std");
const expect = std.testing.expect;
const Foo = struct {
a: u64 = 10,
fn one(self: Foo) u64 {
return self.a + 1;
}
const two = __two;
fn __two(self: Foo) u64 {
return self.a + 2;
}
const three = __three;
const four = custom(Foo, 4);
};
fn __three(self: Foo) u64 {
return self.a + 3;
}
fn custom(comptime T: type, comptime num: u64) fn (T) u64 {
return struct {
fn function(self: T) u64 {
return self.a + num;
}
}.function;
}
test "fn delegation" {
const foo = Foo{};
expect(foo.one() == 11);
expect(foo.two() == 12);
expect(foo.three() == 13);
expect(foo.four() == 14);
}$ zig test var_decl_methods.zig
1/1 test "fn delegation"...OK
All tests passed.
Thank you to Michael Dusan for proposing and implementing this. #3306
Standard Library §
- Sahnvour reworked
std.heap.DirectAllocatorto have more consistent behavior on Windows and POSIX. It no longer needs to be initialized with state; users of the API can now refer directly to a global (thread-safe) instance withstd.heap.direct_allocator. On Windows, rather than being backed byHeapAlloc, it is backed byVirtualAlloc. - Add doc comments for parameters in std.mem.Allocator.
- daurnimator improved
std.os.msghdrdefinition. - LemonBoy implemented
dl_phdr_info std.io.COutStreamgains basic Windows support.- Ryan Liptak reduced the amount of redundant memcpy calls on Windows of
std.heap.DirectAllocator. Previously the memory would be copied to a different aligned address in some cases where the old offset could have been used. This fixes it so that it will always try to use the old offset when possible, and only uses a different offset if the old one is truly invalid (not aligned or not enough space to store the alloc at the old offset). - daurnimator added
std.heap.LoggingAllocator. - Shawn Landden fixed excessive calls to mmap and munmap in
std.heap.DirectAllocator. - daurnimator added
std.os.linux.sendmmsg. - daurnimator added
std.ArrayList.orderedRemove. - tgschultz added
std.PackedIntArrayandstd.PackedIntSlice. - LemonBoy implemented failsafe logic for posixSleep. Now we'll sleep for the specified amount of time
even though the number of seconds doesn't fit in an
isizefield.
After this commit, I made additional modifications:
The sleep APIs are now documented as having spurious wakeups and no precision of timing guaranteed. They also cannot fail. Now, the entire range of u64 is legal to pass tostd.os.time.sleepandstd.os.time.posixSleep. Values that do not fit in the native system APIs will cause a sleep for the longest possible duration and then be handled as a spurious wakeup. - LemonBoy implemented
dl_iterate_phdr. - LemonBoy added
ARCH_SET_*definitions for x86_64. - daurnimator updated Linux syscalls and bits to 5.3. Notably this includes definitions for
io_uring. std.os.closenow usesclose$NOCANCELon Darwin, so that it is impossible to fail.- LemonBoy added sigaltstack syscall.
- LemonBoy fixed
std.os.mprotectsyscall. - emekoi updated std.dynamic_library implementation and improved it to not require an allocator.
- LemonBoy added guard pages for each new thread spawned.
- NBonaparte removed the
MAP_LOCKEDflag from load_dynamic_library and enabled the now passing tests. - tgschultz updated
std.metaandstd.meta.trait"definition" renamed to "declaration", to be inline with the newly clarified semantics of @hasDecl and @hasField. - tgschultz fixed
Deserializer.alignToByte()and added Test Coverage. - emekoi made
windows.unexpectedErrorprint a human friendly string and fixed Windows API function prototypes. - Shawn Landden added a
bcmpimplementation to zig's multi-target libc implementation. This is especially useful because LLVM 9 now emits calls to bcmp. - daurnimator improved
std.testing.expectEqualfor structs. - daurnimator renamed
std.LinkedListtostd.TailQueue. - daurnimator added
std.SinglyLinkedList, and improvedstd.heap.ArenaAllocatorto use a singly linked list instead of double. - daurnimator added
std.http.Headers. - Jonathan Marler removed
constonstd.process.argsAlloc. - daurnimator added gimli permutation to
std.crypto. - Josh Wolfe added
std.mem.concat. - Added missing
error.FileBusytoDeleteFileW's error set. - emekoi added missing
error.FileNotFoundforPATH_NOT_FOUNDinDeleteFileW's error set. - Sam Tebbs added doc comments to alignment functions.
- Update the default panic handler on freestanding. Now the infinite loop has a
@breakpoint()in there. - Sam Tebbs added support for returning
!u8frommain(). std.os.abortno longer calls msvcrt abort() when linking libc. #2071- Added
std.os.windows.subsystem. - daurnimator consolidated Linux
AT_constants because they are the same across architectures. He fixedMAP_definitions to match the kernel. #2837 - Cap getdents length argument to
INT_MAX. The linux syscall treats this argument as having type int, so passing extremely long buffer sizes would be misinterpreted by the kernel. Since "short reads" are always acceptable, just cap it down. - Added
std.fs.updateFile. - Added
std.fs.File.updateTimes. std.os.Statstructs gain methods to abstract over the platform differences with regards tomtime,ctime,atime.- Improved performance of
std.unicode.utf8ToUtf16Le. On a simple test input:original utf8ToUtf16Le: elapsed: 111384483 ns (111 ms)
new utf8ToUtf16Le: elapsed: 138570 ns (0 ms)It's 800x faster in debug mode and ~4500x faster in release-fast mode. This was slowing down installation of files on Windows in build scripts.
- Joachim Henke improved startup code to avoid a register copy when fetching the stack pointer.
- Euan Torano implemented a check for
/dev/urandombeing a character device. This is a security measure.std.os.getrandomwill returnerror.NoDeviceif/dev/urandomis not a character device. - Christoffer Rasmussen fixed
std.rb.Tree.lookupand added Test Coverage. - daurnimator improved
std.elf.Elfopen functions to returnElfstruct directly instead of filling in pointer. #2998 - Euan Torano made
std.os.getrandomon FreeBSD use thegetrandomlibc function. #2993 - Euan Torano fixed the function signature of
std.os.windows.advapi32.RtlGenRandom. #3015 - The return type of
std.math.minon integer operands will now return the type of the smaller integer when possible. - Added
std.c.printf. - daurnimator added
std.BloomFilter. - Shawn Landden modified startup code to avoid providing a
_startfunction when the Root Source File already has one. This enables Zig applications to export their own startup if they wish to take this level of responsibility. - Tetralux added
std.heap.FixedBufferAllocator.reset. - Added
std.os.gethostname. - Added
std.ascii.allocLowerString. - Added
std.ascii.eqlIgnoreCase. - Added
std.ascii.indexOfIgnoreCasePos. - Added
std.ascii.indexOfIgnoreCase.
Debug Info and Stack Traces §
Previously, due to a bug, the stack trace iteration code was using the number of frames collected as the number of frames to print, not recognizing the fixed size of the buffer. So it would redundantly print items, matching the total number of frames ever collected. Now the iteration code is limited to the actual stack trace frame count, and will not print duplicate frames. #2447 #2151
LemonBoy implemented a bunch of fixes for the DWARF parser (#2254):
- Correct parsing of DWARF line_info section.
- Fix reading of udata/sdata encoded attributes.
- Add definition for DW_AT_alignment - even though it's been standardized in DWARF5 some compilers produce it anyway for DWARF4 infos too.
- Fix reading of reference attributes.
- Distinguish between absolute/relative addresses.
- Fix bug in LEB128 parsing.
- Stop emitting
DW_TAG_lexical_blockfor variable declarations. - Made
voida signed type according to DWARF. This follows the convention set by C so that lldb stops complaining about it. - Fixed DIFlags not getting propagated to LLVM.
Thanks to this, and some other debug info fixes from LemonBoy, stack traces now work in release builds:
test.zig
const std = @import("std");
fn foo() !void {
return error.TheSkyIsFalling;
}
pub fn main() !void {
try foo();
}$ zig build-exe test.zig --release-safe
$ ./test
error: TheSkyIsFalling
/home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:4:5: 0x20de63 in std.special.posixCallMainAndExit (test)
return error.TheSkyIsFalling;
^
/home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:8:5: 0x20de6b in std.special.posixCallMainAndExit (test)
try foo();
^
LemonBoy additionally implemented reading symbol names from DWARF sections, so stack
traces on Linux now have actual function names instead of ???'s. You can see
this in the above stack trace.
There is an open issue with this, however - scanning all the symbol names is a bit slow. A future improvement will improve the "Big-O" performance of this function, and bring Zig stack traces up to the same speed that one gets with, for example, gdb.
Despite this trouble for debug info of very large Linux binaries, most projects will experience faster
stack traces thanks to Marc Tiehuis switching to using mmap to read the debug info.
Usually, such techniques are frowned upon due to making error handling difficult, however, this
debug info code is currently designed for the use case of dumping a stack trace when
the application has already panicked, and is about to abort. And so, an error in loading debug
info from disk would be handled by aborting anyway.
In addition, the following improvements were made:
- emekoi fixed debug info on Windows.
- Sahnvour improved correctness of Zig's coff and pdb implementation. It is now able to handle stage1 C++ compiler's PDB and print its stack traces. #2965
- Sahnvour improved stream reading performance of PDB code. Printing stack trace from a stage1 C++ compiler crash is now 10x faster.
- Fixed stack traces on macOS when passing absolute path to root source file. #2700
- Michael Dusan fixed a regression where stack traces stopped working on macOS, and then implemented Test Coverage for stack traces, to catch future regressions. #2485
Formatted Printing §
Marc Tiehuis created a plan to overhaul the std.fmt API. The plan is partially implemented in 0.5.0, but there is remaining work.
The main difference in 0.5.0 is the positional, precision, and width support. This removes the odd width and precision specifiers found and replacing them with the more consistent api described in #1358.
Take the following example:
{1:5.9}
This refers to the second argument (0-indexed) in the argument list. It will be printed with a minimum width of 5 and will have a precision of 9 (if applicable).
Not all types correctly use these parameters just yet. There are still some missing gaps to fill in. Fill characters and alignment have yet to be implemented.
In addition:
- Ryan Liptak added max_depth to
std.fmt.formatto avoid infinite recursion from self-references. - Marc Tiehuis cleaned up fmt.zig with inferred enum literals and split the large test case up.
- tgschultz fixed handling of slices of slices.
- Evan Krause added support for zero-sized structs.
- Michael Dusan fixed
std.fmtto handlestd.SegmentedList:- add guards for use of prealloc_exp in SegmentedList.
- define prealloc_exp even when invalid because std.fmt comptime triggers lazy-init.
- fix std.fmt to print arrays of length 0 as style "[0]<typename>" because "<typename>@address" is n/a without address
- Marc Tiehuis moved pointer parsing out of main state machine. This allows us to
format a pointer with alignment/padding as we would with any other format specifier.
e.g.
{*:5} - Marc Tiehuis made
FormatOptionsarguments non-comptime. No need and should help avoid exessive function monomorphizaton. - Marc Tiehuis passed full options struct to all internal functions. The fill specifier is now handled in some cases. The default fill of '0' is now ' ' for integers and non-byte sequences.
Math §
Marc Tiehuis ported upstream changes from musl's math functions.
This also starts the documentation effort for the math/ subdirectory. The intent is to use this as a somewhat representative test-case for any work on the documentation generator.
- Marc Tiehuis added
std.math.big.Rational. - Marc Tiehuis fixed
std.math.big.Int.toString, as well as handling zero-limb trailing div case and fixing divN/gcdLehmer and fuzz-test failures. - Marc Tiehuis packed
std.math.big.Intsign and length fields, taking it down from 40 to 32 bits on a 64-bit architecture. - Marc Tiehuis added documentation for all functions and algorithm sources.
- Marc Tiehuis corrected
math.nanusage in cos. - Marc Tiehuis fixed exponent calculation in
std.fmt.parseFloat. - Ryan Liptak added
std.math.ceilPowerOfTwoandstd.math.ceilPowerOfTwoPromote. #2426 - Sahnvour allowed parameters of type
comptime_intinstd.math.shlandstd.math.shr. - daurnimator added
std.math.isPowerOfTwo, and Ryan Liptak updated the standard library to take advantage of it.
Thread Local Storage §
Thanks to LemonBoy, Zig now has support for threadlocal variables on
Linux without relying on libc, on the following architectures:
- x86_64
- i386 (new)
- 32-bit ARM (new)
- 64-bit ARM (new)
- RISC-V (new)
- MIPS (new)
In addition to wider architecture support, he implemented on-demand TLS allocation. Previously, if there were too many thread local variables, Zig would panic on startup.
With these changes, Zig has proper support for TLS on Linux.
Reorganization of Operating System Abstractions §
In Zig 0.5.0, OS abstractions are organized in a straightforward manner.
std.os is "Zig-flavored POSIX". All the "bits" familiar to C
programmers are available in this namespace, such as O_RDONLY and
open. The functions have errno translated to Zig errors (on Linux
without libc, there is no thread local variable
for errno 🎉) and slices are used rather than raw pointers where appropriate.
Higher level, cross-platform abstractions are available in category-specific namespaces,
for example std.fs.File.openRead.
std.os.windows has "Zig-flavored Windows", with
GetLastError translated to Zig errors. Raw Windows APIs are available
directly via namespaces named after their DLLs, for example
std.os.windows.kernel32.ExitProcess.
Zig's optional integration with libc is significantly more robust. std.os
functions call libc functions when linking against libc, and otherwise use the
operating system's syscall ABI directly.
After some experimentation, it was concluded that Windows does not integrate well with libc, and so on Windows, even when linking libc, the native Windows API calls are used rather than libc API.
See #2380 for more details and discussion.
Recursive Rewrite of Self-Hosted Parser §
Zig's self-hosted parser is in the standard library - std.zig.parse.
It's the backbone of zig fmt.
I've said before that recursion is one of the enemies of perfect software, because it represents a way that a program can fail with no foolproof way of preventing it. With recursion, pick any stack size and I'll give you an input that will crash your program. Embedded developers are all too familiar with this problem.
It's always possible to rewrite code using an explicit stack using heap allocations, and that's exactly what Jimmi did in the self-hosted parser.
This implementation of the self-hosted parser is an interesting case study of avoiding recursion by using an explicit stack. It is essentially a hand-written recursive descent parser, but with heap allocations instead of recursion. This code is truly a work of art. I like to call it "Jimmi's non-recursive recursive-descent parser".
When Jimmi originally implemented the code, we thought that we could not solve the unbounded stack growth problem of recursion. However, now we have a plan for safe recursion.
And so it was time to lay the code to rest. This is where Stevie Hryciw came in. Stevie rewrote the entire self-hosted parser, to the full grammar specification. This was a large project spanning across several weeks. During this time, Stevie endured painful rebases and dutifully updated the pull request description to keep everyone informed.
Stevie didn't stop there - he followed up by analyzing Performance Impact as well as Readability Impact. He writes:
Performance Impact §
Here are some informal tests of parser_test.zig with perf stat -d on x86_64 Linux.
In the absence of visualizations and more formal testing, some quick findings based on my system:
- CPU cycles, instructions, branches: -55%
- Page faults are roughly the same
- Real time elapsed: -45%
Readability Impact §
Indentation stats of std/zig/parse.zig:
master | stage2-recursive-parser
--------------------------------+---------------------------------
indent count | indent count
------------ | ------------
0 92 | 0 263
1 241 | 1 803
2 123 | 2 763
3 291 | 3 334
4 654 | 4 133
5 662 | 5 60
6 700 | 6 28
7 347 | 7 5
8 229 |
9 42 |
10 18 |
avg indentation level: 4.796999 | avg indentation level: 1.827543
source lines of code: 3399 | source lines of code: 2389
- Maximum indentation level went from 10 to 7
- Average indentation level went from 4.79 to 1.82
- Lines of code (excluding blank/comments) went from 3399 to 2389
Default Segfault Handler §
x86_64-linux and Windows now have, by default, a segfault handler that is attached before main().
Thanks to this, Zig programs now have stack traces for segfaults:
test.zig
pub fn main() void {
dereferenceAPointer(@intToPtr(*i32, 0x1));
}
fn dereferenceAPointer(ptr: *i32) void {
ptr.* = 10;
}$ zig build-exe test.zig
$ ./test
Segmentation fault at address 0x1
/home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:6:13: 0x2281bd in dereferenceAPointer (test)
ptr.* = 10;
^
/home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:2:24: 0x2280fd in main (test)
dereferenceAPointer(@intToPtr(*i32, 0x1));
^
/home/andy/Downloads/zig/lib/std/special/start.zig:194:22: 0x22718b in std.special.posixCallMainAndExit (test)
root.main();
^
/home/andy/Downloads/zig/lib/std/special/start.zig:102:5: 0x22706f in std.special._start (test)
@noInlineCall(posixCallMainAndExit);
^
(process terminated by signal)
This can be disabled:
test.zig
pub const enable_segfault_handler = false;
pub fn main() void {
dereferenceAPointer(@intToPtr(*i32, 0x1));
}
fn dereferenceAPointer(ptr: *i32) void {
ptr.* = 10;
}$ zig build-exe test.zig
$ ./test
(process terminated by signal)
This works because the standard library uses the new feature @import("root") to check for this opt-out.
Thank you to Rocknest for a proof-of-concept implementation of this in #2355.
HashMap and Hashing §
- Ryan Liptak optimized
std.HashMapindexing by avoiding modulo operator.x % ycan be optimized if y is a power of two by doingx & (y-1)instead. HashMap already enforces power of two capacity, so we can take advantage of this optimization. - Ryan Liptak added public ensureCapacity fn to
std.HashMap, and made it round up to the nearest power of two. It now optimizes for the expected count. Finally, he addedputAssumeCapacity. - Josh Wolfe added
std.HashMapAPIs that assert the common case:putNoClobber()forput()removeAssertDiscard()forremove()
- Josh Wolfe added
std.HashMap.getValue(). - Timon Kruiper fixed using
std.AutoHashMapwith anenumkey. #2669 - andersfr added CityHash and Murmur hashing algorithms. #2887
- Sahnvour added forced inlining of integer hashing so that the optimizer can see the fast path based on key's size which is known at comptime. Otherwise it will always outline the call to hasher.update, resulting in much worse performance.
- Sahnvour made wyhash stateless for iterative hashing and small keys.
- Sahnvour made
autoHashmake use of hashing streaming interface. - Sahnvour adapted http/headers.zig to wyhash's new interface.
- Marc Tiehuis added a throughput test program for hash functions.
- Marc Tiehuis added an iterative wyhash API.
Notably, Sahnvour made std.HashMap consistent about whether or not it will
dereference keys. std.AutoHashMap no longer will dereference slices
([]const u8 or []u8), or any pointer type
for that matter:
test.zig
const std = @import("std");
test "AutoHashMap with slices" {
var map = std.AutoHashMap([]const u8, bool).init(std.heap.direct_allocator);
}$ zig test test.zig
/home/andy/Downloads/zig/lib/std/hash/auto_hash.zig:176:9: error: std.auto_hash.autoHash does not allow slices (here []const u8) because the intent is unclear. Consider using std.auto_hash.hash or providing your own hash function instead. Consider std.StringHashMap for hashing the contents of []const u8.
@compileError("std.auto_hash.autoHash does not allow slices (here " ++ @typeName(Key) ++
^
/home/andy/Downloads/zig/lib/std/hash_map.zig:540:21: note: called from here
autoHash(&hasher, key);
^
/home/andy/Downloads/zig/lib/std/hash_map.zig:538:29: note: called from here
fn hash(key: K) u32 {
^
std.StringHashMap is provided for this use case
instead:
string_hash_map.zig
const std = @import("std");
test "StringHashMap" {
var map = std.StringHashMap(bool).init(std.heap.direct_allocator);
_ = try map.put("hello", true);
}$ zig test string_hash_map.zig
1/1 test "StringHashMap"...OK
All tests passed.
Additionally:
- Sahnvour used wyhash in
std.HashMap, and improveautoHashto handle more types and behave more correctly. - Sahnvour added fastpath for
std.mem.eqland simplifiedstd.hash_map.eqlString.
Marc Tiehuis made a series of improvements to hashing performance:
Inline full slice hashing - this gives moderate speed improvements when hashing small keys. The crc/adler/fnv inlining did not provide enough speed up to warrant the change.
Old:
wyhash
small keys: 2277 MiB/s [c14617a1e3800000]
siphash(1,3)
small keys: 937 MiB/s [b2919222ed400000]
siphash(2,4)
small keys: 722 MiB/s [3c3d974cc2800000]
fnv1a
small keys: 1580 MiB/s [70155e1cb7000000]
adler32
small keys: 1898 MiB/s [00013883ef800000]
crc32-slicing-by-8
small keys: 2323 MiB/s [0035bf3dcac00000]
crc32-half-byte-lookup
small keys: 218 MiB/s [0035bf3dcac00000]
New:
wyhash
small keys: 2775 MiB/s [c14617a1e3800000]
siphash(1,3)
small keys: 1086 MiB/s [b2919222ed400000]
siphash(2,4)
small keys: 789 MiB/s [3c3d974cc2800000]
fnv1a
small keys: 1604 MiB/s [70155e1cb7000000]
adler32
small keys: 1856 MiB/s [00013883ef800000]
crc32-slicing-by-8
small keys: 2336 MiB/s [0035bf3dcac00000]
crc32-half-byte-lookup
small keys: 218 MiB/s [0035bf3dcac00000]
Improve siphash performance for small keys by up to 30% (#3124) - this removes the partial buffer handling from the full slice API.
./benchmark --filter siphash --count 1024
Old:
siphash(1,3)
iterative: 3388 MiB/s [67532e53a0d210bf]
small keys: 1258 MiB/s [948c91176a000000]
siphash(2,4)
iterative: 2061 MiB/s [f792d39bff42f819]
small keys: 902 MiB/s [e1ecba6614000000]
New:
siphash(1,3)
iterative: 3410 MiB/s [67532e53a0d210bf]
small keys: 1639 MiB/s [948c91176a000000]
siphash(2,4)
iterative: 2053 MiB/s [f792d39bff42f819]
small keys: 1074 MiB/s [e1ecba6614000000]
Simplify wyhash and improve speed - this removes the exposed stateless variant since the standard variant has similar speed now.
Using ./benchmark --filter wyhash --count 1024, the speed change has
changed from:
wyhash
iterative: 4093 MiB/s [6f76b0d5db7db34c]
small keys: 3132 MiB/s [28c2f43c70000000]
to
wyhash
iterative: 6515 MiB/s [673e9bb86da93ea4]
small keys: 10487 MiB/s [28c2f43c70000000]
Documentation §
- There is now a CONTRIBUTING.md.
- Added docs: Enum Literals
- Added docs: usingnamespace
- @truncate docs are updated and corrected. #2234
- Matt Stancliff fixed a test in the langref to assert against modified var.
- Ryan Liptak added README instructions for making changes to the standard library. #2324
- Ryan Liptak fixed README instructions for the filepath to test file when testing std lib changes.
- Add note to @setRuntimeSafety.
- Remove
@setGlobalLinkagesection.@setGlobalLinkagewas removed with #462. The not-yet-implemented proposal for external weak symbols is #1917. - Shritesh Bhattarai improved the language reference to show the
-targetcommand line argument. - Many contributors fixed typos in small, easy-to-merge pull requests. Thank you for that.
- Shritesh Bhattarai added lib codeblock type to docgen and used it for wasm32-freestanding.
- Shritesh Bhattarai added recommendation to use optional pointers for nullptrs instead of
allowzero. - Shritesh Bhattarai added a comment about
for elseandbreak. - Jonathan Pentecost updated
for elseexample. #2614 - emekoi added missing syntax blocks.
- Michael Dusan clarified struct size and ABI-alignment.
- Corrected documentation regarding mixing object files. #2905
- Retire the
example/folder. Code moved to become standalone tests. #2759 - JohnathanFL added a multidimensional array example.
- Added operations list to @atomicRmw.
- Aaron Klapatch added documentation for field access to C pointers. #3088
- Shritesh improved the README instructions for macOS to use the LLVM path provided by homebrew instead of a hardcoded value that became out-of-date.
- Vesa Kaihlavirta shortened @field documentation and added an example.
- Jay Weisskopf updated README headline to match website, and made links underlined on hover.
- Duncan added a favicon to the language reference.
- Shawn Landden updated @clz and @ctz, clarifying terminology to not be endian-specific.
zig build §
zig build is still in an experimental, proof-of-concept phase, and will remain
that way until at least the package manager is complete.
However, there were still improvements to zig build
this release cycle:
- emekoi forwarded error code on non-successful exits of child processes to
zig build. - install is now the default step; the default prefix is zig-cache. #2817
zig buildnow searches up the directory hierarchy forbuild.zig. #2587- Added
setLibCFileAPI - Install .pdb files along with binaries. #2848
- Added
standardTargetOptionsand deprecatedsetTargetin favor ofsetTheTarget. - Added Valgrind CLI options.
- Support
DESTDIRenvironment variable. #2929 - Nick Erdmann fixed a regression in the stack checking option.
- Benjamin Feng added
builder.findProgramtest and fixed references. - Made install prefix available to build.zig and prevented accident of
'/'showing up in application/library names. linkSystemLibraryintegrates withpkg-config.- Build scripts can now take advantage of QEMU and Wine integration by doing
artifact.enable_qemu = true;andartifact.enable_wine = true;, respectively. - Added ability to override the dynamic linker of an executable artifact.
zig fmt §
zig fmt now fixes invalid whitespace instead of rejecting it.
The // zig fmt: off directive is ignored for whitespace fixes.
- Raul Leal added support for comptime blocks in containers. #2308
- Various bugs fixed, more Test Coverage.
- Timon Kruiper corrected formatting for multiline string in arrays.
- Timon Kruiper added LineComment support and test case for when MultiLines are used in ArrayInit.
- Vexu fixed comment formatting in arrays and fn params.
- Vexu fixed IfTypeExpr parsing.
- Vexu improved comment indentation in arrays.
- Vexu fixed comments getting removed after empty comments.
- Stevie Hryciw fixed crash when file ends with struct field.
- Benjamin Feng fixed zig fmt reinterpreting
a && basa & &b. Now it has a helpful error message: "`&&` is invalid. Note that `and` is boolean AND.". #2660 - Timon Kruiper fixed an integer overflow in zig fmt and added testcase.
- Vexu implemented Async Functions syntax, including
noasync. - yvt improved the handling of
// zig fmt: off/on. - Hang Shick Pak fixed nested
if.
Thanks to WebAssembly Support improvements, there is also a web-based formatter made by Shritesh Bhattarai.
libc §
Zig now provides libc for the following targets (find this information with zig targets):
- aarch64_be-linux-gnu
- aarch64_be-linux-musl
- aarch64_be-windows-gnu
- aarch64-linux-gnu
- aarch64-linux-musl
- aarch64-windows-gnu
- armeb-linux-gnueabi
- armeb-linux-gnueabihf
- armeb-linux-musleabi
- armeb-linux-musleabihf
- armeb-windows-gnu
- arm-linux-gnueabi
- arm-linux-gnueabihf
- arm-linux-musleabi
- arm-linux-musleabihf
- arm-windows-gnu
- i386-linux-gnu
- i386-linux-musl
- i386-windows-gnu
- mips64el-linux-gnuabi64
- mips64el-linux-gnuabin32
- mips64el-linux-musl
- mips64-linux-gnuabi64
- mips64-linux-gnuabin32
- mips64-linux-musl
- mipsel-linux-gnu
- mipsel-linux-musl
- mips-linux-gnu
- mips-linux-musl
- powerpc64le-linux-gnu
- powerpc64le-linux-musl
- powerpc64-linux-gnu
- powerpc64-linux-musl
- powerpc-linux-gnu
- powerpc-linux-musl
- riscv64-linux-gnu
- riscv64-linux-musl
- s390x-linux-gnu
- s390x-linux-musl
- sparc-linux-gnu
- sparcv9-linux-gnu
- wasm32-freestanding-musl
- x86_64-linux-gnu
- x86_64-linux-gnux32
- x86_64-linux-musl
- x86_64-windows-gnu
musl 1.1.23 §
Zig ships with the source code to musl. Zig 0.5.0 updates to the 1.1.23 release of musl, plus a handful of patches to fix various issues with 64-bit ARM Support and RISC-V Support. All these patches are merged into musl upstream and will be part of the next release.
Previously, the way that Zig built musl from source skipped some necessary files. LemonBoy solved this issue.
Additionally, the updating process for musl was brittle and confusing. Now, the process is streamlined and fully documented. Unnecessary patches were dropped.
Now, Zig has excellent Test Coverage of musl. The Zig test suite tests building musl for these targets:
- x86_64-linux-musl
- aarch64v8_5a-linux-musl
- arm32v8_5a-linux-musleabihf
- mipsel-linux-musl
glibc 2.30 §
In Zig 0.5.0, not only can Zig provide dynamically linked glibc for any target, but it can also provide any version of glibc for any target:
getrandom.zig
const std = @import("std");
pub fn main() void {
var buf: [16]u8 = undefined;
_ = std.c.getrandom(&buf, buf.len, 0);
}
$ ./zig build-exe test.zig -lc -target x86_64-linux-gnu -target-glibc 2.24
lld: error: undefined symbol: getrandom
>>> referenced by test.zig:5 (/home/andy/dev/zig/build/test.zig:5)
>>> ./test.o:(main.0)
$ ./zig build-exe test.zig -lc -target x86_64-linux-gnu -target-glibc 2.25
$ ./test
$
Updating to the newest glibc version is now streamlined and fully documented. Even though Zig now supports every version of glibc, the amount of bytes required for a Zig installation with regards to glibc has decreased, because a dummy libc file is no longer required, as it is generated on-the-fly depending on the target version selected.
The target glibc version is exposed in @import("builtin") and is used by
the Standard Library to do a glibc version check, to decide whether to use libc
getrandom or read from /dev/urandom. #397
The supported glibc version range is increased to include 2.30.
Building glibc now has Test Coverage when the -Denable-qemu and
-Denable-foreign-glibc options are enabled, for these targets:
- x86_64-linux-gnu
- aarch64v8_5a-linux-gnu
- arm32v8_5a-linux-musleabihf disabled due to #3287
Zig ships with mingw-w64 §
Zig now ships with the source code and header files to mingw-w64 (version 6.0.0), and uses this to provide libc when targeting Windows.
Combining this with Wine, one can cross-compile C code for Windows and run it, without even touching a Windows computer:
hello_windows.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello Windows\n");
return 0;
}
$ zig build-exe --c-source hello_windows.c -lc -target x86_64-windows-gnu
$ wine hello_windows.exe
Hello Windows
Building and linking against mingw-w64 libc now has Test Coverage
for the x86_64-windows-gnu target.
One of the use cases for this is creating Zig packages out of C libraries, and there is now a proof of concept of this with SDL2.
The open-source game Legend of Swarkland is being written in Zig, and it uses this SDL2 package in order to support cross compiling for Windows with nothing installed other than Zig. The developer, Josh, does not have a Windows computer to test on, but has a brother who uses Windows he wants to be able to playtest his game. Using only Zig and Wine, Josh can create Windows builds of his game and even test them, before sending his brother an executable.
$ git clone https://github.com/thejoshwolfe/legend-of-swarkland --recursive
$ cd legend-of-swarkland
$ zig build -Dtarget x86_64-windows-gnu
$ ls zig-cache/bin
legend-of-swarkland.exe legend-of-swarkland_headless.pdb
legend-of-swarkland_headless.exe legend-of-swarkland.pdb
Integration with mingw-w64 is easy and clean, because their headers are already
multi-architecture. Thank you especially to IRC user wbs, who is
largely responsible for that, and for patiently helping me
work through my own issues caused by hacking up the mingw-w64 build system to
integrate into Zig.
Freestanding libc §
Zig provides libc even when compiling in freestanding mode. This enables some C libraries to work even when there is no host Operating System.
In Zig 0.5.0, this concept is a little bit more fleshed out and clear. One can observe this freestanding libc in action when building C code for WebAssembly.
- Added strcmp, strncmp, strerror, strlen.
- Shawn Landden added fma and fmaf.
There is still a lot to do on this front, and it could be an engaging project for contributors.
C Translation §
LemonBoy made several improvements:
- conversion to/from floating point types.
- parsing float/double literals.
- converting char literals.
- support for integer to boolean conversions.
- pointer to/from integral conversion.
- translation from pointer to boolean.
- improved support for pointer casting with different alignment.
Thanks to C pointers supporting optional syntax,
NULL pointers now translate to null.
Additionally:
- handle int to ptr and ptr to int casting. #2451
- added enough C tokenization/parsing to handle shifting in macros. #2451
- better detection of pointer to struct demoted to opaque.
- Vexu fixed translation of escape sequences.
- Gustav Olsson forwarded framework dirs to embedded Clang in addition to linker on macOS.
Self-Hosted C Translation §
In Zig 0.4.0, the translate-c and @cImport implementations are 5,000 lines of C++.
However, in this release, Zig is transitioning to a fully self-hosted implementation.
The parts of translate_c.cpp that interact with the Clang C++ API have been extracted
into zig_clang.h and zig_clang.cpp. This is a C API on top of the C++ API
with some careful static assertions to ensure the file is kept up-to-date as Clang's C++ API changes.
translate_c.cpp now interacts with the Clang C++ API exclusively via zig_clang.h.
These files are generally useful for any project and they are MIT licensed.
Based on zig_clang.h, clang.zig is created, updated, and maintained. This is
extern functions and types so that Zig code can utilize the C layer on top of the Clang C++ API.
And with this, we have src-self-hosted/translate_c.zig which is the self-hosted implementation
of translate-c (and @cImport). This is exposed with zig translate-c-2.
Until the self-hosted implementation is brought up to feature parity, zig translate-c
and @cImport are still the C++ implementation. This work is partially done thanks to Stevie Hryciw; you can get a sense of progress by
examining the test cases.
More contributions welcome!
See #1964 for more details.
compiler-rt §
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 gcc 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.
- @divTrunc rather than @divFloor in
__muloti4. - Added __muldi3 and __aeabi_lmul.
- Carter Sande added support for thumb versions older than armv6.
- Robin Voetter added __aeabi_read_tp.
- vegecode improved support for thumb and added:
- __aeabi_dcmp
- __aeabi_fcmp
- __comparedf2
- __comparesf2
- LemonBoy added:
- __aeabi_d2f
- __aeabi_f2d
- __aeabi_i2d
- __aeabi_i2f
- __aeabi_idiv
- __aeabi_idiv
- __aeabi_idiv
- __aeabi_idivmod
- __aeabi_l2d
- __aeabi_ldivmod
- __aeabi_ui2d
- __aeabi_ul2d
- __aeabi_unwind_cpp_pr0
- __aeabi_unwind_cpp_pr1
- __aeabi_unwind_cpp_pr2
- __ashlti3
- __ashrti3
- __divdi3
- __divmoddi4
- __divmodsi4
- __divsi3
- __divsi3
- __divsi3
- __extendsfdf2
- __floatdidf
- __floatsidf
- __floatsisf
- __floatsitf
- __floatundidf
- __floatunsidf
- __lshrti3
- __moddi3
- __modsi3
- __mulodi4
- __truncdfsf2
- __umodsi3
LemonBoy also fixed an edge case in addXf3 - since the operands may be zero,
he used the wrapping operators to avoid a spurious integer-overflow error. He then proceeded
to other fixes:
- Fixed float comparison result in
__aeabi_{f,d}cmp*. - Avoid endless recursion in
__extendhfsf2using @bitCast.
With Zig 0.5.0, compiler-rt much more complete, but not fully. There are some missing functions, and it's planned to do an audit before 1.0.
Test Coverage §
Zig now has much more exhaustive test coverage of foreign architectures, thanks to QEMU. These new options are available to zig build when running the Zig test suite:
-Denable-qemu- runs cross-compiled compiler-rt, behavior, and std lib tests on foreign architectures.-Denable-wine- runs cross-compiled compiler-rt, behavior, and std lib tests with Wine.-Denable-foreign-glibc=path- if you have builds of glibc for other architectures available, this enables testing cross-compiled compiler-rt, behavior, and std lib tests on foreign architectures, dynamically linking glibc.-Dskip-non-native- skips all non-native tests.-Dskip-libc- if you don't want to wait for e.g. musl to build.
See CONTRIBUTING.md for more details.
Michael Dusan implemented a new kind of test coverage for stack traces, to catch future regressions in Debug Info and Stack Traces.
Additionally:
- Sahnvour implemented slightly better output for failure of tests based on text comparison.
- Compile error tests no longer pointlessly run in all build modes, speeding up tests.
- Assertions are now always enabled in the stage1 C++ compiler, and have stack traces when there is debug info available.
- LLVM IR verification is now always enabled in the stage1 C++ compiler.
- The self-hosted compiler is no longer being built. It's planned to resume work on the self-hosted compiler in the next release cycle.
- zig build scripts can take advantage of QEMU and Wine integration by doing
artifact.enable_qemu = true;andartifact.enable_wine = true;, respectively. - Windows CI image is updated to MSVC 2019. Build mode is changed to MinSizeRel to work around an MSVC bug. #3024
std.debug.global_allocator is deprecated as far as being used in tests
is concerned. Tests should use std.heap.FixedBufferAllocator and stack memory
instead.
Miscellaneous Improvements §
- freestanding target adds -ffreestanding to cc parameters. #2287
- hryx synchronized the grammar with the spec.
- @sizeOf is now defined to return
0forcomptimetypes. This defines @sizeOf to be the runtime size of a type, which means that it is zero for types such as comptime_int, type, and (enum literal). #2209 - Shawn Landden removed shebang (
#!) support. #2165 - rylmovuk changed symbol name of tests in codegen.
Tests now have the symbol name of the format
test "<name>"in order to be more easily distinguished from functions with similar names. #2267 - Dong-hee Na changed the CLI parameters
--enable-picand--disable-picto-fPICand-fno-PIC. - When using @memset to set bytes to
undefined, Zig notices this case and does a single Valgrind client request rather than N. Speeds up all allocators in safe modes. #2388 - LemonBoy implemented stack probes for x86/x86_64 on non-Windows operating systems. Windows already had stack probes. This feature guarantees stack overflow to result in a segmentation violation when a function has a very large stack frame in danger of skipping over the guard page.
- Added
--bundle-compiler-rtlinker option. - Added compile error for attempt to cast enum literal to error. #2203
- When Zig crashes with an assertion failure, many assertions now will print the Zig source code location that was related to the compiler bug. This helps users work around compiler bugs and helps with producing small test cases for filing a bug report.
- Jimmi Holst Christensen contributed fixes and simplifications for stage 1 parser. This solved the grammar ambiguity with enum literals inside array literals and struct literals. #2235
- LemonBoy implemented validation for enum tags of extern enums. The C specification mandates the enum to be compatible with signed char, signed int or unsigned int. He also implemented support for signed types as enum tags.
- LemonBoy improved assembly compilation to use
zig ccrather than concatenating all assembly blocks into one large string and handing that to LLVM. This fixed bugs as well as enabling C preprocessor support for assembly files. - Fix static builds of zig from requiring C compiler to be installed when linking libc.
When zig links against libc, it requires a dynamic linker path. Usually this can be determined based on the architecture and operating system components of the target. However on some systems this is not correct; because of this zig checks its own dynamic linker.
When zig is statically linked, this information is not available, and so it resorts to using cc -print-filename=foo to find the dynamic linker path.
Before this change, Zig incorrectly exited with an error if there was no C compiler installed. Now, Zig falls back to the dynamic linker determined based on the arch and os when no C compiler can be found.
- LemonBoy added a warning for when run/test is paired with emit options.
- emekoi added Windows subsystem to
@import("builtin"). - mrkishi added cache-control headers to tarballs on CI, making downloads faster and saving server costs.
- Sahnvour implemented the Sfc64 RNG from PractRand.
- No more redundant safety check when switching on tagged unions. Should improve debug build performance by a tiny bit.
- Better CLI error message for missing sub-architecture.
std/special/bootstrap.zigis renamed tostd/special/start.zig. This makes a bit more sense, and actually is kinda important to make sense because it shows up in stack traces. Further, the logic on whether to include this file is improved.- Zig now prints the directory name if it fails to write to the cache directory. #2429
- Michael Dusan added the missing help for
--override-lib-dir. - There is now a
lib/directory that mirrors the directory tree of installed files, andstd/is moved into it appropriately. These changes led to Self-Hosted Installation of Library Files. - daurnimator added a cmake option to allow user to select static vs dynamic LLVM.
- Zig now calculates alignment of types itself rather than relying on LLVM. This fixes alignment of 128-bit integers, which is needed for @cmpxchgWeak and @cmpxchgStrong to function correctly. It also is one step in the direction of Zig having another backend besides LLVM.
- In the stage1 C++ compiler,
AstNodeobjects have asrc()method to print the corresponding source file, line number, and column number. This is handy when using a debugger. - Jonathan Marler improved the C++ stage1 compiler's internal BigInt API to assert on unexpected data loss from casting.
- Michael Dusan enhanced stage1 debug printing of Zig IR:
- pass2 now prints missing instructions in a trailing fashion.
- instruction struct name added to print as column 2.
- print fn name in pass1.
- replace scalar with enum IrPass for clarity.
- Added ability to specify darwin framework search dirs.
- The CLI now accepts
-lparameters as an alias for--library. As an example, one may specify-lcrather than--library c. - Sahnvour added
/debug:fastlinkwhen building with MSVC and debug info. - LemonBoy improved the C++ stage1 compiler to recognize and skip the UTF-8 Byte Order Mark.
- ScorrMorr changed ZigList::append in the C++ stage1 compiler to pass param as ref. Appears to be about a 2% improvement in performance.
- daurnimator updated the C++ stage1 compiler to use
zig_panicrather than having LLVM abort. - When
--test-cmdis provided tozig test, it will run it regardlesso of whether the binary is native. This enables, for example, testing with QEMU. - Added
-DCLI parameter for setting C preprocessor definitions.
Self-Hosted .d File Parsing §
When Zig compiles C code (using libclang), it automatically enables .d file generation so that Zig can learn the dependencies and do proper caching.
Previously, Zig's .d file parser was written in C++ and a bit brittle. Michael Dusan dove head-first into this and implemented a robust .d file parser, in self-hosted Zig code, complete with unit tests.
Unfortunately, there is still an open issue regarding this, because the first line Clang outputs cannot be parsed unambiguously. Clang Bug Report. If you, the reader of these release notes, are a Clang developer, please fix 🙏
Binary Size §
@import("builtin") gained strip_debug_info which is
a comptime bool value telling whether --strip was passed to the
compiler.
This makes Zig code aware at compile-time of when it will not have any debug information available at runtime. The standard library now takes advantage of this to avoid Zig binaries containing useless debug info code.
This, along with Timon Kruiper's contribution of enabling the equivalent of
-ffunction-sections in Zig's LLVM codegen, resulted in tiny
ReleaseSmall binaries:
hello.zig
const std = @import("std");
pub fn main() void {
std.debug.warn("Hello, World!\n");
}
$ zig build-exe hello.zig --release-small --strip --single-threaded
$ ./hello
Hello, World!
$ ls -ahl ./hello
-rwxr-xr-x 1 andy users 10K Sep 26 15:56 ./hello
$ ldd ./hello
not a dynamic executable
The Windows build is even smaller:
$ zig build-exe hello.zig --release-small --strip --single-threaded -target x86_64-windows
$ wine64 ./hello.exe
Hello, World!
$ ls -ahl ./hello.exe
-rwxr-xr-x 1 andy users 3.0K Sep 26 15:57 ./hello.exe
Zig's ability to create tiny executables is especially attractive for WebAssembly.
Self-Hosted Installation of Library Files §
Previously, when editing source files, it was required to make install
(msbuild -p:Configuration=Release INSTALL.vcxproj on Windows) in order
to test changes. This used cmake's install() function for all the library files,
such as the Standard Library, as well as the libc files that Zig
ships with. Unfortunately, this printed something like this every time:
-- Installing: /home/andy/dev/zig/build/lib/zig/std/array_list.zig
-- Installing: /home/andy/dev/zig/build/lib/zig/std/ascii.zig
-- Installing: /home/andy/dev/zig/build/lib/zig/std/atomic/int.zig
(...snip...)
...for all 6,091 lib files. Even when the files are already installed, it would print:
-- Up-to-date: /home/andy/dev/zig/build/lib/zig/std/array_list.zig
-- Up-to-date: /home/andy/dev/zig/build/lib/zig/std/ascii.zig
-- Up-to-date: /home/andy/dev/zig/build/lib/zig/std/atomic/int.zig
(...snip...)
There is no way to disable this in cmake. This caused make install
on my Linux computer to take 2.4 seconds even when it has to do nothing, and prints all these
unnecessary lines to stdout. On my Windows it took even longer, upwards of 5 seconds.
Now, installation of lib files is self-hosted, using zig build. Running
make when there is nothing to do takes 0.3 seconds on my Linux computer;
2.4 on Windows.
Unfortunately, lib file installation
happens in the make target instead of the make install target, because
cmake has no way to add a custom command to the install target. So that's why Zig now has the
option -DZIG_SKIP_INSTALL_LIB_FILES=ON, which is recommended to enable for
contributors to Zig. It's off by default because otherwise installing Zig would be missing
library files. However when contributing to Zig, running the zig binary from
the build directory will search upwards for library files and find them directly in the source tree.
This means one can directly edit the Standard Library in the source tree, and changes
will be picked up without needing to run make at all.
Bug Fixes §
- Shritesh Bhattarai fixed zig run not passing the exec path.
- Fixed 128-bit integer multiplication on Windows.
- LemonBoy fixed comptime shift-right. #2225
-
Ben Noordhuis fixed an error with the cache system where it would incorrectly
close the cache file descriptor, causing a file descriptor race condition when
the descriptor got reused, leading to:
Warning: Unable to write cache file [..]: unexpected seek failure
Ben took care to make os_file_close poison the file handle after closing, to help track down future use-after-close bugs. - Fixed issue with @setEvalBranchQuota causing assertion failure. #2261
- Fixed Debug mode when error return tracing is off. #2276
- Ryan Liptak fixed DirectAllocator not unmapping unused pages on large alignments. #2306
- Ryan Liptak fixed heap allocators when shrinking an object but growing its alignment.
- Ryan Liptak fixed aligned reallocs on Windows with std.heap.DirectAllocator.
- hryx fixed tag expression incorrectly being accepted for structs.
- Michael Dusan added
-fvisibility-inlines-hiddento the build flags. On macOS building with Xcode/clang the linker complains loudly when symbol visibility is inconsistent. This option syncs visibilty setting of both LLVM and Zig. - tgschultz spotted and LemonBoy fixed Undefined Behavior invoked in the compiler's internal BigInt right shift operation.
- Fixed path canonicalization when $HOME has a trailing slash.
- Shritesh Bhattarai fixed docgen not properly closing tags for skipped execs.
- LemonBoy fixed generation of container initializers. #1636 #1608
- Matt Stancliff fixed a crash in command line argument parsing.
- LemonBoy fixed erroneous test case regarding
std.io.InStream. - Michael Dusan fixed compile error for loop expr val ignored. #2460
- Fix hang for some compile errors. #2467
- LemonBoy fixed
clock_gettimeon Linux systems without VDSO. - daurnimator fixed
std.heap.FailingAllocatornot counting allocations. - Tyler Philbrick fixed a memory leak in parser tests. See the commit message for a nice explanation.
- LemonBoy fixed fixed formatting for multiline asm expressions.
- Sam Tebbs fixed zig crashing after a tokenization error.
- LemonBoy fixed VDSO calls not using the C calling convention.
- LemonBoy fixed load/store of non-integer fields in packed struct.
- LemonBoy fixed too eager comptime evaluation of error ptr.
- LemonBoy fixed build-lib on macOS. Due to a typo, Zig was not setting
K_DARWINas expected on the archive file. This allowed removing macOS-specific linking hacks. - LemonBoy fixed signedness mismatch in comparison in os.cpp.
- emekoi fixed Zig not respecting the subsystem flag in all cases.
- Fix invalid LLVM IR generated for
?*voidconst casts. #2578 - LemonBoy fixed a crash when there are no namespace components. #2500
- LemonBoy added a compile error for
undefinedused as a type. #2436 - Nick Erdmann fixed debug rendering of character literals <= 0x0f.
- Sam Tebbs added a parser check for null body in
if,for, andwhile. - Fixed @export not working for arrays, and allow specifying link sections on
externvariables. Additionally fixed @export not respecting the name parameter. Previously, the variable name would be used instead. #2679 - Fixed compiler crash due to not resolving alignment of child types when slicing if custom alignment is provided.
- Fixed compiler crash with @typeInfo on extern lib name.
- Jonathan Marler fixed duplicate exe name with
zig run. - Jonathan Marler fixed Windows create process retry/path search.
- joachimschmidt557 fixed
std.atomic.Queue.isEmpty. - Matthew Murray fixed a regression with
std.math.absFloatand added tests. - Michael Dusan fixed a compile error when building zig with Clang.
- Luna provided a quickfix on non-existing
std.net.Address.family. - Compile error for using slice as array init expr type when there are more than 0 elements. #2764
- Fixed
switchwithnulland T peer types and inferred result location type. #2762 - Fixed
forwithnulland T peer types and inferred result location type. #2762 - Michael Dusan fixed a stack escape in
add_source_file(). - Added missing compile error for comptime continue inside runtime catch. #2604
- dimenus fixed regression with static linking against MSVCRT. #2064
- Michael Dusan fixed a bug with array multiplication and functions in comptime blocks. #2916
- Michael Dusan fixed assigning an array with a non-const element value to another array (or vector) causing a segfault. #2942
- Michael Dusan updated
std.rb.Node.getParentto return optional, fixing #2962. - Ryan Saunderson modiifed header precedence for
zig cc, resolves intrinsics issues. #3027 - Fixed enum with one member and custom tag type.
- Fixed build errors due to vendored LLD being incorrectly dynamically linked.
- yvt corrected LLVM subarch names for ARM.
- Vexu added a compile error for incorrect atomic ordering in @fence. #3082
- Nick Erdmann corrected the error message when dependency requires Position Independent Code.
- Fixed
build-objnot working with C files that use libc. #3093 - Fixed assigning a struct into an array defined using
**causing a segfault when run. #3095 - Fixed
voidarray as a local variable initializer. #1767 - Michael Dusan fixed @bitCast segfault with literal array param. #3010
- Fixed @bitCast of packed struct literal. #3042
- Timon Kruiper added compiler error when variable in asm template cannot be found. #3030
- Euan Torano fixed
std.os.getrandomnot filling the entire buffer when requesting larger than a 32-bit integer. #3012 - Jonathan Marler fixed a regression with the way LLVM types are handled causing a compiler
crash (#3058). He notes:
One interesting thing here is that if the compiler was written in Zig this bug likely never would have happened as Zig would protect you from dereferencing a NULL pointer like this. The tag_type field would either be an optional type which would require testing for NULL before using it, or it wouldn't be optional and couldn't be set or initialized to NULL.
- Fixed implicit cast from zero sized array ptr to slice. #1850
- Michael Dusan fixed stage1 build regression on macOS + xcode/clang.
- Michael Dusan improved building Zig from tarball source rather than git repository.
- quiet
fatal: not a git repositorymessage. - if git probe fails skip ZIG_VERSION modification.
- quiet
- Vesa Kaihlavirta fixed some test cases of
std.fmt.parseFloatnot parsing correctly. - LemonBoy fixed lazy values not getting resolved when checking for definedness. #3154
- LemonBoy fixed assertions tripped from not resolving struct field types. #3143
- Timon Kruiper added compile error when shifting amount is not an int type.
- The C++ stage1 compiler now raises the maximum file descriptor limit by doing a
binary search for the maximum
RLIMIT_NOFILE. Patch lifted from node.js commit 6820054d2d42ff9274ea0755bea59cfc4f26f353. Thanks to Ben Noordhuis for the suggestion and patch. - Timon Kruiper added compiler error when negating invalid type.
- emekoi fixed compiler error for gcc 9.2.0.
- Sahnvour added a compile error forbidding opaque types in function return types.
- LemonBoy forced LLVM to generate byte-aligned packed unions. Sometimes the frontend and LLVM would disagree on the ABI alignment of a packed union. Solve the problem by telling LLVM we're gonna manage the struct layout by ourselves. #3184
- LemonBoy made @cDefine accept
voidargument. #2612 - LemonBoy resolved lazy arguments passed to @compileLog. #3193
- Michael Dusan fixed build on macOS + xcode + clang.
- LemonBoy corrected
AT_FDCWDdefinition. - Michael Dusan fixed gcc 9.1.0 compiler error.
- Timon Kruiper fixed comptime bitcast inside an expression.
- Sahnvour added compile error rejecting types of automatic container layout in packed unions.
- Fixed invalid
tailattribute for @panic causing undefined behavior in the stage1 C++ compiler when it hit asserts, causing it to print garbage memory to the terminal. #3262 - LemonBoy fixed generation of tail fields for
packed struct. - LemonBoy corrected stack alignment for new stack.
- Michael Dusan fixed lost argv[0] in stage1 C++ compiler, manifesting on
FreeBSD when using
zig cc. - Fix regression causing stage1 C++ compiler crash regarding external types.
- LemonBoy fixed computation of switch coverage. #3258
- LemonBoy fixed crash with invalid extern type. #3240
- Jay Weisskopf made zig create the user-specified
output-dirif it does not exist. #2637 - LemonBoy fixed assignment to optional payload. #3081
- LemonBoy fixed llseek behavior.
- LemonBoy
usingnamespacecausing error for redeclaration for the same var node. #3316 - LemonBoy added safety for truncating
mmap2offsets if not multiple of page size. - LemonBoy corrected calculation of padding length in struct, making sure the resulting type is in-sync with the one produced and used by LLVM. #3138
This Release Contains Bugs §
Zig has known bugs.
Zig is immature. Even with Zig 0.5.0, working on a non-trivial project using Zig will likely mean participating in the development process.
The first release that will ship with no known bugs will be 1.0.0.
Roadmap §
The major theme of next release cycle will be safety.
Along with this, it's planned to resume work on the self-hosted compiler now that new Async Functions are done.
Issues that have the possibility of breaking changes to the language will be prioritized, so that the language can be stabilized.
Package Manager Status §
Having a package manager built into the Zig compiler is a long-anticipated feature. Zig 0.5.0 does not have this feature, however the Zig project now that Async Functions are complete, it's time to begin on networking in the Standard Library. I expect to complete this along with at least an early prototype of the package manager during the next release cycle.
Accepted Proposals §
Here are proposals that have been accepted during the 0.5.0 release cycle, to give you an idea of the upcoming changes to Zig:
- Saturating arithmetic
- Make function definitions expressions
- make implicit cast syntax different than function call syntax
- wrapping negation -% should work on unsigned ints
- introduce operating system versions as part of the target
- support top level fields
- add implicit cast from [*c]T to E!*T
- allow unicode characters in character literals
- Allocator interface: make shrinkFn optional
- terminology update: use the phrase "detectable illegal behavior" rather than "safety-checked undefined behavior"
- Add a way to globally have @setFloatMode set for the whole project
- introduce the concept of "logging" to the standard library
- @reduce builtin for turning a SIMD vector into a scalar value
- make the C pointer type always a compile error unless the file declares that it is generated code
- Allow COFF output for freestanding target
Active Open-Source Projects Using Zig §
- Oxid - an arcade-style game where you fight waves of monsters in a fixed-screen maze.
- zootdeck - the linux desktop fediverse reader.
- FUN✜DUDE - WebAssembly Gameboy emulator.
- pluto - kernel written almost entirely in Zig and supports x86, with aarch64 and x64 backends being planned.
- UEFI-paint - UEFI-bootable touch paint app.
- zig-bare-metal-raspberry-pi - demo bare-metal raspberry pi program written in zig.
- Legend of Swarkland - Hack-n-slash roguelike inspired by NetHack.
- PrismJS now supports Zig - JavaScript-based syntax highlighter for web pages.
Funding Status §
During this release cycle, I joined the GitHub Sponsors program. This transition went fairly smoothly, thanks to Devon Zuegel. It's pretty clear to me that she's going above and beyond what's professionally required of her to help maintainers get sponsored.
At this point, funding for the Zig project is stable. There are enough funds that I can continue to work full time on Zig without burning down my savings. Based on current trends, I should even be able to get health insurance soon.
However, community growth has outpaced funding growth. My job has become more and more demanding over time. Even just merging pull requests at this point is a full time job. I have averaged merging 1.5 pull requests per day into Zig for the last 4 years. There is more than enough work for 2 full-time developers on Zig, and I would love to get to the point where paying another full-time developer is possible.
To facilitate this, I'm planning on starting Zig Software Foundation non-profit organization. I am looking for recommendations for a lawyer who would help me set this up. If you know one, please send me an email.
Special thanks to those who sponsor Zig. Because of you, Zig is not driven by the needs of a business; instead it exists solely to serve the open source community.
- Stevie Hryciw
- mbarkhau
- Wesley Hill
- 419928194516
- Vesa Kaihlavirta
- THFKA4
- ryanworl
- iohzrd
- Ross Kilgariff
- Donald Harris
- Rickard Andersson
- Filippo Valsorda
- Dzmitry Lahoda
- Jeff Kelley
- tav
- Karrick McDermott
- Shritesh
- Neil Wang
- qbradley
- Zach Feldman
- Felix Yuan
- Sean Jense
- mschwaig
- Jay Weisskopf
- Champ Yen
- Loris Cro
- Josh Tobin
- Charles Palmer
- Yaroslav Zhavoronkov
- Christoffer Rasmussen
- Ashe Connor
- Jesse Meyer
- Simon Cruanes
- tschaei
- Omar Akkila
- rtroberts
- S-D-
- BenoitJGirard
- Jason Merrill
- Mitch Small
- dbandstra
- Brian Orr
- RaniSharim
- Steve Perkins
- redj
- Jeff Fowler
- John Schmidt
- via
- Jimmy Zelinskie
- Brian Mitchell
- Dan Boykis
- Mirek Rusin
- Audun Wilhelmsen
- Vladimir Vissoultchev
- Neil Henning
- Thomas Ballinger
- Lukas Attridge
- Steve Ray
- Will Sommers
- Jethro Nederhof
- Jack Halford
- Hong Shick Pak
- Jimmi Holst Christensen
- Haze Booth
- Johann Muszynski
- Eric
- Luis Alfonso Higuera Gamboa
- Łukasz Adamczak
- Clement 'cmc' Rey
- Hasan Yasin Öztürk
- Abdulrhman A. AlKhodiry
- Emily A. Bellows