0.5.0 Release Notes

Download & Documentation

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.

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.

A support table for master branch can be found on the home page. Here the support table for 0.5.0 is reproduced:

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.

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.

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 targets is guaranteed to include this target.

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 targets will display the target if it is available.

will 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 asm and cannot emit object files.

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:

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

"); 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

" ); }

$ 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.

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.

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)

(no libc) aarch64v8_5a-linux-musl (building musl 1.1.23 from source, statically linking against it, and using it for OS APIs)

(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 undefined ptr.

ptr. 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:

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.

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.mmap use SYS_mmap2 if it exists.

use if it exists. Used sys_*stat*64 instead of sys_*stat* where appropriate.

instead of 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:

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:

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

stratact made the following changes:

Added missing <stdint.h> include for uint8_t type declaration.

include for type declaration. Added needed FreeBSD check to link to libpthread.

Added missing C dl_iterate_phdr function 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:

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_size for WebAssembly

for 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-exe is for executables which have a main() . build-lib is for building libraries of functions to use from, for example, a web browser environment.

is for executables which have a . is for building libraries of functions to use from, for example, a web browser environment. For now pass --export-all for libraries when there are any C objects because we have no way to detect the list of exports when compiling C code.

for 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-entry to the linker for executables. If you want --no-entry then use build-lib .

to the linker for executables. If you want then use . Made the "musl" ABI the default ABI for wasm32-freestanding.

build-exe does include the startup code that supplies _start for the wasm32-freestanding target. Previously this did not occur because of logic excluding "freestanding".

does include the startup code that supplies for the target. Previously this did not occur because of logic excluding "freestanding". build-lib for wasm32-freestanding target gets linked by LLD. To avoid infinite recursion, compiler-rt and Freestanding libc are built as objects rather than libraries.

for target 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.zig producing "libfoo.a", now it produces "foo.wasm".

producing "libfoo.a", now it produces "foo.wasm". Freestanding libc only provides _start symbol 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.

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:

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.

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)

(no libc) mipsel-linux-musl (building musl 1.1.23 from source, statically linking against it, and using it for OS APIs)

(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:

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.

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

" ); }

$ 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

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_void pointer.

pointer. Sam Tebbs added implicit cast for *[N]T to [*c]T .

to . Stevie Hryciw made anyerror no longer a keyword. #2835

no longer a keyword. #2835 @cmpxchgStrong and @cmpxchgWeak now support 128-bit integers on x86_64.

comptime_int now implicit casts to comptime_float .

now implicit casts to . @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 noinline keyword for function declarations.

keyword for function declarations. Comparison between union tag and enum literal is now allowed thanks to LemonBoy. #2810

LemonBoy made Single-element enum default to u0 . However, comptime_int is still allowed as an explicit enum tag type. #2997

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.

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 ( 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" )); assert( @hasDecl (Foo, "hi" )); 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.

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; test "C pointers with optional syntax" { const ptr1 = getenv( c"HOME" ).?; const ptr2 = getenv( c"HOME" ) orelse return error .Homeless; if (getenv( c"HOME" )) |ptr3| { } const ptr4 = getenv( c"HOME" ); if (ptr4 == null ) { } }

$ 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 ; test "C pointers with optional syntax" { const ptr1 = getenv( c"HOME" ).?; const ptr2 = getenv( c"HOME" ) orelse return error .Homeless; if (getenv( c"HOME" )) |ptr3| { } const ptr4 = getenv( c"HOME" ); if (ptr4 == null ) { } }

$ zig test getenv2.zig -lc 1/1 test "C pointers with optional syntax"...OK All tests passed.

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 { const fd = try os.open( "/dev/urandom" , os.O_RDONLY, 0 ); defer os.close(fd); var buf: [ 100 ] u8 = undefined ; const nbytes = try readBlocking(fd, &buf); } fn readBlocking (fd: os.fd_t, buffer: [] u8 ) ! usize { return std.os.read(fd, buffer) catch |err| switch (err) { error .WouldBlock => unreachable , 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 { const fd = try os.open( "/dev/urandom" , os.O_RDONLY, 0 ); defer os.close(fd); var buf: [ 100 ] u8 = undefined ; const nbytes = try readBlocking(fd, &buf); } 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: {}

" , 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} .

Shawn Landden improved the consistency of the names and parameters of bit manipulation intrinsics.

@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)

#2119 #2120

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

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.

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.

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" ); 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.

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" { 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.

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.

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:

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 ( 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.

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 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

" ); _ = async func(); std.debug.warn( "resume func

" ); resume frame; std.debug.warn( "end main

" ); } fn func () void { std.debug.warn( "begin func

" ); frame = @frame (); suspend ; std.debug.warn( "end func

" ); }

$ 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; const simulate_fail_download = false ; const simulate_fail_file = false ; const suspend_download = true ; const suspend_file = true ; pub fn main () void { _ = async amainWrap(); 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!

" ); } 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

" ); 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

" ); 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 await s 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.

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 comptime implementation.

implementation. Shawn Landden added @splat.

See #903 for more details.

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 ( 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:

type

noreturn

void

bool

Integers- The maximum bit count for an integer type is 65535 .

. Floats

Pointers

comptime_int

comptime_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 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

Sahnvour reworked std.heap.DirectAllocator to 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 with std.heap.direct_allocator . On Windows, rather than being backed by HeapAlloc , it is backed by VirtualAlloc .

to 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 with . On Windows, rather than being backed by , it is backed by . Add doc comments for parameters in std.mem.Allocator.

daurnimator improved std.os.msghdr definition.

definition. LemonBoy implemented dl_phdr_info

std.io.COutStream gains basic Windows support.

gains 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).

. 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.PackedIntArray and std.PackedIntSlice .

and . 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 isize field.

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 to std.os.time.sleep and std.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.

field. 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 to and . 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.

definitions for x86_64. daurnimator updated Linux syscalls and bits to 5.3. Notably this includes definitions for io_uring .

. std.os.close now uses close$NOCANCEL on Darwin, so that it is impossible to fail.

now uses on Darwin, so that it is impossible to fail. LemonBoy added sigaltstack syscall.

LemonBoy fixed std.os.mprotect syscall.

syscall. 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_LOCKED flag from load_dynamic_library and enabled the now passing tests.

flag from load_dynamic_library and enabled the now passing tests. tgschultz updated std.meta and std.meta.trait "definition" renamed to "declaration", to be inline with the newly clarified semantics of @hasDecl and @hasField.

and "definition" renamed to "declaration", to be inline with the newly clarified semantics of @hasDecl and @hasField. tgschultz fixed Deserializer.alignToByte() and added Test Coverage.

and added Test Coverage. emekoi made windows.unexpectedError print a human friendly string and fixed Windows API function prototypes.

print a human friendly string and fixed Windows API function prototypes. Shawn Landden added a bcmp implementation to zig's multi-target libc implementation. This is especially useful because LLVM 9 now emits calls to bcmp.

implementation to zig's multi-target libc implementation. This is especially useful because LLVM 9 now emits calls to bcmp. daurnimator improved std.testing.expectEqual for structs.

for structs. daurnimator renamed std.LinkedList to std.TailQueue .

to . daurnimator added std.SinglyLinkedList , and improved std.heap.ArenaAllocator to use a singly linked list instead of double.

, and improved to use a singly linked list instead of double. daurnimator added std.http.Headers .

. Jonathan Marler removed const on std.process.argsAlloc .

on . daurnimator added gimli permutation to std.crypto .

. Josh Wolfe added std.mem.concat .

. Added missing error .FileBusy to DeleteFileW 's error set.

to 's error set. emekoi added missing error .FileNotFound for PATH_NOT_FOUND in DeleteFileW 's error set.

for in '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.

in there. Sam Tebbs added support for returning ! u8 from main() .

from . std.os.abort no longer calls msvcrt abort() when linking libc. #2071

no 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 fixed MAP_ definitions to match the kernel. #2837

constants because they are the same across architectures. He fixed 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.

. 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.Stat structs gain methods to abstract over the platform differences with regards to mtime , ctime , atime .

structs gain methods to abstract over the platform differences with regards to , , . 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.

. 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/urandom being a character device. This is a security measure. std.os.getrandom will return error .NoDevice if /dev/urandom is not a character device.

being a character device. This is a security measure. will return if is not a character device. Christoffer Rasmussen fixed std.rb.Tree.lookup and added Test Coverage.

and added Test Coverage. daurnimator improved std.elf.Elf open functions to return Elf struct directly instead of filling in pointer. #2998

open functions to return struct directly instead of filling in pointer. #2998 Euan Torano made std.os.getrandom on FreeBSD use the getrandom libc function. #2993

on FreeBSD use the libc function. #2993 Euan Torano fixed the function signature of std.os.windows.advapi32.RtlGenRandom . #3015

. #3015 The return type of std.math.min on integer operands will now return the type of the smaller integer when possible.

on 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 _start function 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.

function 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 .

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_block for variable declarations.

for variable declarations. Made void a signed type according to DWARF. This follows the convention set by C so that lldb stops complaining about it.

a 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

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.format to avoid infinite recursion from self-references.

to 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.fmt to handle std.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

to handle : 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 FormatOptions arguments non- comptime . No need and should help avoid exessive function monomorphizaton.

arguments non- . 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.

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.

, as well as handling zero-limb trailing div case and fixing divN/gcdLehmer and fuzz-test failures. Marc Tiehuis packed std.math.big.Int sign and length fields, taking it down from 40 to 32 bits on a 64-bit architecture.

sign 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.nan usage in cos.

usage in cos. Marc Tiehuis fixed exponent calculation in std.fmt.parseFloat .

. Ryan Liptak added std.math.ceilPowerOfTwo and std.math.ceilPowerOfTwoPromote . #2426

and . #2426 Sahnvour allowed parameters of type comptime_int in std.math.shl and std.math.shr .

in and . daurnimator added std.math.isPowerOfTwo , and Ryan Liptak updated the standard library to take advantage of it.

Thanks to LemonBoy, Zig now has support for threadlocal variables on Linux without relying on libc, on the following architectures:

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.

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.

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:

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%

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

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.

Ryan Liptak optimized std.HashMap indexing by avoiding modulo operator. x % y can be optimized if y is a power of two by doing x & (y- 1 ) instead. HashMap already enforces power of two capacity, so we can take advantage of this optimization.

indexing by avoiding modulo operator. can be optimized if y is a power of two by doing 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 added putAssumeCapacity .

, and made it round up to the nearest power of two. It now optimizes for the expected count. Finally, he added . Josh Wolfe added std.HashMap APIs that assert the common case: putNoClobber() for put() removeAssertDiscard() for remove()

APIs that assert the common case: Josh Wolfe added std.HashMap.getValue() .

. Timon Kruiper fixed using std.AutoHashMap with an enum key. #2669

with an key. #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 autoHash make use of hashing streaming interface.

make 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 improve autoHash to handle more types and behave more correctly.

, and improve to handle more types and behave more correctly. Sahnvour added fastpath for std.mem.eql and simplified std.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]

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 @setGlobalLinkage section. @setGlobalLinkage was removed with #462. The not-yet-implemented proposal for external weak symbols is #1917.

section. was removed with #462. The not-yet-implemented proposal for external weak symbols is #1917. Shritesh Bhattarai improved the language reference to show the -target command line argument.

command 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 else and break .

and . Jonathan Pentecost updated for else example. #2614

example. #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

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 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 build now searches up the directory hierarchy for build.zig . #2587

now searches up the directory hierarchy for . #2587 Added setLibCFile API

API Install .pdb files along with binaries. #2848

Added standardTargetOptions and deprecated setTarget in favor of setTheTarget .

and deprecated in favor of . Added Valgrind CLI options.

Support DESTDIR environment variable. #2929

environment variable. #2929 Nick Erdmann fixed a regression in the stack checking option.

Benjamin Feng added builder.findProgram test and fixed references.

test and fixed references. Made install prefix available to build.zig and prevented accident of '/' showing up in application/library names.

showing up in application/library names. linkSystemLibrary integrates with pkg-config .

integrates with . Build scripts can now take advantage of QEMU and Wine integration by doing artifact.enable_qemu = true ; and artifact.enable_wine = true ; , respectively.

and , respectively. Added ability to override the dynamic linker of an executable artifact.

zig fmt now fixes invalid whitespace instead of rejecting it. The 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 && b as a & &b . Now it has a helpful error message: "`&&` is invalid. Note that `and` is boolean AND.". #2660

as . 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 .

. Hang Shick Pak fixed nested if .

Thanks to WebAssembly Support improvements, there is also a web-based formatter made by Shritesh Bhattarai.

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

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

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:

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

"); 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.

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.

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.

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 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 __extendhfsf2 using @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.

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.

- 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.

- 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.

- 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.

- 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 ; and artifact.enable_wine = true ; , respectively.

and , 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.

freestanding target adds -ffreestanding to cc parameters. #2287

hryx synchronized the grammar with the spec.

@sizeOf is now defined to return 0 for comptime types. 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

for types. 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

) 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

in order to be more easily distinguished from functions with similar names. #2267 Dong-hee Na changed the CLI parameters --enable-pic and --disable-pic to -fPIC and -fno-PIC .

and to and . 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

, 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-rt linker option.

linker 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 cc rather 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.

rather 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.

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.zig is renamed to std/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.

is renamed to . 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, and std/ is moved into it appropriately. These changes led to Self-Hosted Installation of Library Files.

directory that mirrors the directory tree of installed files, and 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, AstNode objects have a src() method to print the corresponding source file, line number, and column number. This is handy when using a debugger.

objects have a 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 -l parameters as an alias for --library . As an example, one may specify -lc rather than --library c .

parameters as an alias for . As an example, one may specify rather than . Sahnvour added /debug:fastlink when building with MSVC and debug info.

when 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_panic rather than having LLVM abort.

rather than having LLVM abort. When --test-cmd is provided to zig test , it will run it regardlesso of whether the binary is native. This enables, for example, testing with QEMU.

is provided to , it will run it regardlesso of whether the binary is native. This enables, for example, testing with QEMU. Added -D CLI parameter for setting C preprocessor definitions.

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 