0.4.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 even if they dislike it.

This release features 6 months of work and changes from 46 different contributors, spread among 875 commits.

Special thanks to my sponsors who provide financial support. You're making Zig sustainable.

This release of Zig upgrades to LLVM 8. Zig operates in lockstep with LLVM; Zig 0.4.0 is not compatible with LLVM 7.

Notably this means that Zig now has WebAssembly Support, as well as recognizing the HermitCore and Hurd operating systems. On the other hand, support for the Nios II architecture is dropped, due to it being an experimental target which was largely unmaintained.

Marc Tiehuis started an initial FreeBSD branch. Greg V picked up the figurative baton in #1661, and Marcio Giaxa completed the effort.

Now, FreeBSD works in the stage1 C++ compiler code, as well as the Zig Standard Library, which means it now has Tier 2 Support. To achieve Tier 1 Support, these things are needed:

Implement building the FreeBSD libc startup files

Get all the tests passing. The CI does not have all the tests enabled for FreeBSD.

Despite these issues, thanks to SourceHut, FreeBSD does have some CI testing support, as well as automated binary builds available for x86_64 on the download page.

See #1759 for more details.

Maya Rashish ported the stage1 C compiler as well as the Zig Standard Library to be compatible with NetBSD. Have a look at their writeup: Porting Zig to NetBSD - a fun, speedy port

NetBSD is limited to Tier 2 Support due to lack of a continuous integration service, as well as a missing implementation of building the system's libc startup files lazily from source.

Now that Zig uses LLVM 8, the wasm32 target architecture is guaranteed to be available in Zig.

Ben Noordhuis fixed zig build-exe for the wasm32 target (#1570).

Shritesh Bhattarai added --allow-undefined and --export-all to the wasm linker line, solving #1622. There may be a better way to catch link errors in the future, but for now, this makes WebAssembly work out-of-the-box in Zig:

extern fn print ( i32 ) void ; export fn add (a: i32 , b: i32 ) void { print(a + b); }

$ zig build-exe math.zig -target wasm32-freestanding

test.js

const fs = require('fs'); const source = fs.readFileSync("./math"); const typedArray = new Uint8Array(source); WebAssembly.instantiate(typedArray, { env: { print: (result) => { console.log(`The result is ${result}`); } } }).then(result => { const add = result.instance.exports.add; add(1, 2); });

$ node test.js The result is 3

There is also the "WebAssembly System Interface" operating system target, wasi . I haven't really explored this use case yet, but I did make the C integer types work for it. More research and exploration is needed.

Shawn Landden implemented arm64 Linux support in the Zig Standard Library, as well as the necessary stage1 C++ compiler changes.

The arm64-linux target has now graduated to Tier 2 Support. To achieve Tier 1 Support, we would need some way to run Continuous Integration tests on arm64 linux.

SourceHut has experimental arm64 Debian images, which may be worth looking into.

GitHub user nebulaeonline approached the Zig project with an interesting use case: creating a UEFI application.

nebulaeonline submitted a pull request adding UEFI as a new operating system that Zig recognizes. The patch contained the linker configuration and a few updates to the Zig Standard Library.

With this patch merged, nebulaeonline went on to work on their UEFI barebones project. It looks like they ended up using C rather than Zig in the end, most likely due to Zig's lack of maturity. Here I have linked to the last revision before they removed the Zig code, in case anyone wants to poke around. Since then Zig has gained C Pointers which was a large pain point for nebulaeonline.

Zig now uses a "support tier" system to communicate the level of support for different targets.

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 ziglang.org/download with links to pre-built binaries.

These targets have debug info capabilities and therefore produce stack traces on failed assertions.

(coming soon) 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.

One of the main features of Zig is @cImport and C Translation. This is accomplished by linking against Clang libraries. Clang is a fully featured C and C++ compiler. Zig has all this functionality - so we may as well expose it!

The zig cc command exposes clang, which you can see with this amusing output:

$ zig cc --version clang version 8.0.0 ...

zig cc is a bit of a low level command. The higher level command line interface --c-source (or the corresponding Zig Build System API) is recommended instead, for a few reasons:

Compilers are core system components and often have compatibility patches to make them work on a given system. For example, a fresh build of clang from source does not work out-of-the-box on NixOS, because it is not aware of the location of the system libc. However if you install clang through the system package manager, everything works as expected, due to the patches. zig cc represents a fresh build of clang, which does not have such patches. This is actually A Good Thing - more on this below.

represents a fresh build of clang, which does not have such patches. This is actually A Good Thing - more on this below. The higher level interface has a consistent set of defaults for both C code and Zig code. For example, when using --c-source : Automatic dependency tracking and caching. See Build Artifact Caching. -march=native is enabled for the native target. Parameters such as -target , --color , and --strip will apply to both C and Zig Code. Zig has more capabilities as a linker driver than Clang. Zig ships with libc. Consistent features enabled or disabled such as stack protection, position independent code, and whether the frame pointer is omitted.

:

Here's an example of Zig building some C code:

hello.c

#include <stdio.h> int main(int argc, char **argv) { printf("Hello world

"); return 0; }

$ zig build-exe --c-source hello.c --library c $ ./hello Hello world

You can use --verbose-cc to see what C compiler command this executed:

$ zig build-exe --c-source hello.c --library c --verbose-cc zig cc -MD -MV -MF zig-cache/tmp/42zL6fBH8fSo-hello.o.d -nostdinc -fno-spell-checking -isystem /home/andy/dev/zig/build/lib/zig/include -isystem /home/andy/dev/zig/build/lib/zig/libc/include/x86_64-linux-gnu -isystem /home/andy/dev/zig/build/lib/zig/libc/include/generic-glibc -isystem /home/andy/dev/zig/build/lib/zig/libc/include/x86_64-linux-any -isystem /home/andy/dev/zig/build/lib/zig/libc/include/any-linux-any -march=native -g -fstack-protector-strong --param ssp-buffer-size=4 -fno-omit-frame-pointer -o zig-cache/tmp/42zL6fBH8fSo-hello.o -c hello.c -fPIC

Note that if I run the command again, there is no output, and it finishes instantly:

$ time zig build-exe --c-source hello.c --library c --verbose-cc real 0m0.027s user 0m0.018s sys 0m0.009s

This is thanks to Build Artifact Caching. Zig automatically parses the .d file that clang produces and uses a robust caching system to avoid duplicating work.

In the Zig Build System, builder.addCExecutable used to be a system C compiler driver. That functionality is deleted. Instead, build scripts can attach C source files to any executable, library, or object with foo.addCSourceFile . You can see a nice example of this in Michael Dusan's benchmark.unicode project.

Don't forget to use foo.linkSystemLibrary("c"); if the C code expects to use libc.

You may have noticed that the Zig-generated C compilation command included -nostdinc .

This is a crucial step in the direction Zig is headed - providing consistent, reliable builds that are insulated from the system-specific differences in the wild.

This affects @cImport and C Translation as well. By default, Zig no longer looks in system paths for C header files, instead relying only on what Zig ships and what users explicitly request with -isystem .

However, when one uses the Zig Build System, and uses linkSystemLibrary API, Zig takes that as a hint to look in system default search paths. Marc Tiehuis proposed to expose this functionality to the command line interface, and that is likely to be an accepted proposal.

Thanks to Akuli for adding /lib/x86_64-linux-gnu or similar to default system library search paths for zig build scripts.

One piece to this puzzle is that Zig now ships with libc. You can find the available libc targets with zig targets :

... Available libcs: aarch64_be-linux-gnu aarch64_be-linux-musl aarch64-linux-gnu aarch64-linux-musleabi armeb-linux-gnueabi armeb-linux-gnueabihf armeb-linux-musleabi armeb-linux-musleabihf arm-linux-gnueabi arm-linux-gnueabihf arm-linux-musleabi arm-linux-musleabihf i386-linux-gnu i386-linux-musl 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 riscv32-linux-musl riscv64-linux-gnu riscv64-linux-musl s390x-linux-gnu s390x-linux-musl sparc-linux-gnu sparcv9-linux-gnu x86_64-linux-gnu x86_64-linux-gnux32 x86_64-linux-musl

What this means is that --library c for these targets does not depend on any system files!

Let's look at that C hello world example again:

$ zig build-exe --c-source hello.c --library c $ ./hello Hello world $ ldd ./hello linux-vdso.so.1 (0x00007ffd03dc9000) libc.so.6 => /lib/libc.so.6 (0x00007fc4b62be000) libm.so.6 => /lib/libm.so.6 (0x00007fc4b5f29000) libpthread.so.0 => /lib/libpthread.so.0 (0x00007fc4b5d0a000) libdl.so.2 => /lib/libdl.so.2 (0x00007fc4b5b06000) librt.so.1 => /lib/librt.so.1 (0x00007fc4b58fe000) /lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc4b6672000)

glibc does not support building statically, but musl does:

$ zig build-exe --c-source hello.c --library c -target x86_64-linux-musl $ ./hello Hello world $ ldd hello not a dynamic executable

In this example, Zig built musl libc from source and then linked against it. The build of musl libc for x86_64-linux remains available thanks to the caching system, so any time this libc is needed again it will be available instantly.

This means that this functionality is available on any platform. Windows and macOS users can build Zig and C code, and link against libc, for any of the targets listed above. Similarly code can be cross compiled for other architectures:

$ zig build-exe --c-source hello.c --library c -target aarch64v8-linux-gnu $ file hello hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 2.0.0, with debug_info, not stripped

In some ways, Zig is a better C compiler than C compilers!

This functionality is more than bundling a cross-compilation toolchain along with Zig. For example, the total size of libc headers that Zig ships is 22MiB uncompressed. Meanwhile, the headers for musl libc + linux headers on x86_64 alone are 8MiB, and for glibc are 3.1MiB (glibc is missing the linux headers), yet Zig currently ships with 40 libcs. With a naive bundling that would be 444MiB. However, thanks to this process_headers tool that I made, and some good old manual labor, Zig binary tarballs remain roughly 30MiB total, despite supporting libc for all these targets, as well as compiler-rt, libunwind, and libcxx, and despite being a clang-compatible C compiler. For comparison, the Windows binary build of clang 8.0.0 itself from llvm.org is 132MiB.

Note that only the Tier 1 Support targets have been thoroughly tested. It is planned to add more libcs (including for Windows), and to add test coverage for building against all the libcs. Support for building the FreeBSD libc startup files is the main feature needed to achieve Tier 1 Support for the target.

Thanks to Jay Weisskopf for helping with the glibc targets by removing an obsolete compat shim file.

Although Zig ships with libc for some targets, it still supports building against the system-native libc.

0.4.0 introduces a new command zig libc which prints the various paths of libc files. It outputs them to stdout in a simple text file format that it is capable of parsing. You can use zig libc libc.txt to validate a file.

Here's what it looks like on my NixOS laptop:

$ zig libc # The directory that contains `stdlib.h`. # On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` include_dir=/nix/store/q2q1sg5sljia8sihhwcpbxir70yw33bw-glibc-2.27-dev/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=/nix/store/q2q1sg5sljia8sihhwcpbxir70yw33bw-glibc-2.27-dev/include # The directory that contains `crt1.o`. # On POSIX, can be found with `cc -print-file-name=crt1.o`. # Not needed when targeting MacOS. crt_dir=/nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib # 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=

And here's what it looks like on my Windows laptop:

> zig.exe libc # The directory that contains `stdlib.h`. # On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` include_dir=C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17134.0\ucrt # 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=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726\lib\x64\\..\..\include # The directory that contains `crt1.o`. # On POSIX, can be found with `cc -print-file-name=crt1.o`. # Not needed when targeting MacOS. crt_dir=C:\Program Files (x86)\Windows Kits\10\\Lib\10.0.17134.0\ucrt\x64\ # The directory that contains `vcruntime.lib`. # Only needed when targeting MSVC on Windows. msvc_lib_dir=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726\lib\x64\ # The directory that contains `kernel32.lib`. # Only needed when targeting MSVC on Windows. kernel32_lib_dir=C:\Program Files (x86)\Windows Kits\10\\Lib\10.0.17134.0\um\x64\

These arguments are gone:

--libc-lib-dir [path] directory where libc crt1.o resides --libc-static-lib-dir [path] directory where libc crtbegin.o resides --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides

Instead we have this argument:

--libc [file] Provide a file which specifies libc paths

This is used to pass a libc text file (which can be generated with zig libc ). So it is easier to manage multiple cross compilation environments.

When Zig must depend on the native libc installation, it first looks for zig-cache/native_libc.txt . If found, this file will be used. If this file does not exist or has a parse error, Zig will perform native libc detection and then overwrite this file. This can save time, because native libc detection can be time consuming. For example it may need to invoke the system C compiler several times to find out paths of things, or it may invoke some Windows COM API which is a bit non-deterministic in terms of performance.

The --static CLI option is removed and the -dynamic option is added.

Instead of an explicit --static flag, Zig makes things as static as possible by default. Even zig build-lib will now create a static library by default, but -dynamic can be used to choose a dynamic library.

When targeting Linux, Zig now automatically links statically when not linking against any shared libraries. It also disables Position Independent Code in this case, which improves performance, especially with regards to Thread-Local Variables. --enable-pic and --disable-pic can be used to override the default, but this should be a rare use case.

Position Independent Code is also disabled by default for the freestanding OS target. When targeting Windows, Position Independent Code is always enabled.

It is proposed to rename these options to -fPIC and -fno-PIC to match C compilers.

For some use cases, it is known at compile-time that a given application or executable will never participate in multi-threading. For these use cases Zig has Single-Threaded Builds.

There is now a compile option --single-threaded which has the following effects:

All Thread Local Variables are treated as Global Variables.

The overhead of Coroutines becomes equivalent to function call overhead. Note this will not be implemented until the upcoming Coroutine Rewrite

The @import ( "builtin" ).single_threaded becomes true and therefore various userland APIs which read this variable become more efficient: std.Mutex becomes an empty data structure and all of its functions become no-ops. John Schmidt made std.heap.ThreadSafeFixedBufferAllocator alias std.heap.FixedBufferAllocator when --single-threaded . #1910 Shritesh Bhattarai made std.os.exit use the exit_group syscall when not single-threaded.

becomes and therefore various userland APIs which read this variable become more efficient:

The feature is exposed in the Zig Build System with foo.single_threaded = true;

When one invokes the zig build command, this executes a user-defined build script. This is called the zig build system. It received a few improvements during this release cycle.

Notably, Build Artifact Caching is enabled by default. This means that by default build artifacts are now output into the zig-cache directory. The output path is not predictable and if the build script desires to run an executable, rather than hard coding a path and using builder.addCommand , build scripts must use executable.run API, or addArtifactArg . An example of this can be found in the tetris example.

builder.addSystemCommand is available to run commands that are not created by the build system itself. An example of this can be found in the Raspberry Pi OS example.

Another option build scripts have is to specify the output directory of an artifact with artifact.setOutputDir . This disables caching and makes artifact.getOutputPath , artifact.getOutputLibPath , and artifact.getOutputHPath available.

Finally, the best way to make the output path of an artifact predictable is to install it with the build system's install step. There are some ergonomics and defaults to improve with this strategy, which will be tackled during the next release cycle.

Please note that Zig does no cache evicting yet. You may have to manually delete zig-cache directories periodically to keep disk usage down. It's planned for this to be a simple Least Recently Used eviction system eventually.

--output , --output-lib , and --output-h are removed. Instead, use --output-dir which defaults to the current working directory. Or take advantage of --cache on , which will print the main output path to stdout, and the other artifacts will be in the same directory with predictable file names. --disable-gen-h is available when one wants to prevent .h file generation.

@cImport is always independently cached now. It always writes the generated Zig code to disk which makes debug info and compile errors better. No more "TODO: remember C source location to display here" #2015. The --verbose-cimport command no longer dumps Zig AST to stderr; instead it prints the file paths of the generated .zig files for inspection. In addition to this improved behavior, the common case of @cImport with all the C header files unchanged is now fast due to being cached, yet still correct if any of the C files change.

In addition, these improvements were made:

Zig no longer creates "temporary files" other than inside a zig-cache directory.

Zig uses os_self_exe_path to determine exe path not argv[0]

to determine exe path not Tests as part of the build system support all of the same API as executables, libraries, and objects.

linkLibrary will make the target depend on libc if the source does.

will make the target depend on libc if the source does. When building C source file(s), don't try to generate .h files.

Added builder.addFmt API and use it to test stage1 zig fmt. #1968

API and use it to test stage1 zig fmt. #1968 Fixed build.zig not respecting --static . #2027

. #2027 Shritesh Bhattarai gave usageAndErr a clean exit which makes zig build --help more friendly. (#2194)

more friendly. (#2194) Ruslan Prokopchuk implemented support for LibExeObjStep.disable_gen_h . It is sometimes useful to skip generating of the header file (e.g. #2173), and zig compiler provides an option --disable-gen-h to control that behaviour. However, setting lib.disable_gen_h = true in a typical build.zig didn't append the option to arguments. This commit fixes it and adds a convenient setDisableGenH setter.

. It is sometimes useful to skip generating of the header file (e.g. #2173), and zig compiler provides an option to control that behaviour. However, setting in a typical didn't append the option to arguments. This commit fixes it and adds a convenient setter. Windows doesn't have rpaths for DLLs so we instead add search paths to Path environment variable when running an executable that depends on DLLs built with zig build.

Zig comes with a robust caching system. When compiling Zig code, Zig knows all the files that it depends on, and when compiling C code, Zig uses -MV -MD -MF parameters to Clang and then parses the generated .d file.

The other component of this is the "compiler id" concept:

$ zig id Cxb7A-sAjt6VDq35pHZj3ACcwiSFqcCndt0NJkdQQlZVKolwm-QKnce1KEMUTqPr

This is the hash of the compiler binary itself, plus all the dynamic libraries it depends on, directly or indirectly. That means if you fix a bug in memcpy in the system libc, Zig will detect this change and the value of zig id will be different. However, the value is quickly computed because it also participates in the caching system.

Here is a "manifest file" in the caching system for zig id :

4850313 1554740311 416647968 cOOP0xpsfiYfuMJCS3V_fLKdNmxFxLih_UACAUqwynGp1Y616NJ2lHL0hOpWOp_o /home/andy/dev/zig/build/zig 3077799 1 0 bBgfkt9ZSVN_HgBbzwHhzKmQxpssdDYq_HcbEkdu7KDcUhanATUER2_5l_U__9DE /nix/store/5dphwv1xs46n0qbhynny2lbhmx4xh1fc-zlib-1.2.11/lib/libz.so.1 1165671 1 0 jSVrSQyNkPFoz0Z6GesezdVP1ZBRpHL2UbbX7HbNEprcU4aBuIyG9z6bAOvvPZr0 /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/librt.so.1 1165626 1 0 scJm8rYLhDLp_SDXmbvdlcj9fEWQadEm_kHUBJn2oGnht3pSh2MNzsUJZjR1wwa1 /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/libdl.so.2 1165664 1 0 pGDNQn5x_j1NAWzjh4AOUP8OKyJj1KSPd4SJYzRVbTTp7TRb4IQpDs7ZAU3muqfz /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/libpthread.so.0 1165631 1 0 4tmxoKlbVwB42gYxT7B3CKIBXpbGdb_vuqfYiXlOZdyngmFJdQ7DBZxWqZKSO0Ss /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/libm.so.6 3077645 1 0 dxe6qw_yMCi8Bp7ocrVcl2XMdRlKfa0S3ATqbxsEMISRQdQ1dyxTEE1uDbv0R54z /nix/store/sf0wnp30savqz9ljn6fsrn8f63w5v0za-gcc-7.4.0-lib/lib/libstdc++.so.6 1165630 1 0 5boikua2jQubswAtj8MICJzjmGNUxTZrNreQm_DmtspgRsXULyCkyUUMYcNFamII /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/libgcc_s.so.1 1165616 1 0 qiXB510da3ryfL7LcPUvL2EQgY2tIwszLRvgQdDrQtnWVl4zA1Nfpq4SDpIz3IYW /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/libc.so.6 1165607 1 0 MpHwajxu3h0TtsHViKMAmHHeGMbi9IuDLlybX6PpqT66Xfa1h2GAT3XFIX19dEz- /nix/store/fivq0nbggp4y8mhy3ixprqd7qyn1hy2j-glibc-2.27/lib/ld-linux-x86-64.so.2

The fields are, respectively:

inode mtime seconds mtime nanoseconds blake hash of file contents path to file

This same system is used for build artifact caching. For example, here is the manifest file for hello world in Zig (when built with --cache on ):

5505089 1548876783 204467480 rcnRrxBgZSiWki_XN9XKlQ2yfWkM6KLYhUWprzniEBtjgmeUSmtlv5mAguA4l2Q1 /home/andy/dev/zig/example/hello_world/hello.zig 4850617 1554305190 0 GXP-IfocPj934vVJYrccBKIVoOcWULaUtkBoR-iHnEKEIoXtdchyuQi8tk8FTncS /home/andy/dev/zig/build/lib/zig/std/std.zig 5114024 1551557778 0 6u-vFMcHH_FcfkAyqCcoYXAfF2TLMCkoP86Q3Ykecmage7_E4ObRsfP52-YXDHH6 /home/andy/dev/zig/build/lib/zig/std/special/panic.zig 4850617 1554305190 0 GXP-IfocPj934vVJYrccBKIVoOcWULaUtkBoR-iHnEKEIoXtdchyuQi8tk8FTncS /home/andy/dev/zig/build/lib/zig/std/std.zig 4850277 1553179780 0 9S5aQg0ORbkQnvDCVeW2aNPSeCu993I4iO3JoGrif4py7zBS4si4mCaeHHuo9TEn /home/andy/dev/zig/build/lib/zig/std/debug.zig 4849956 1554500038 0 8c76uDlKxK5jZ1L0Uup4Lu0y1NlU5-KrUCKFyZnzoi7Hv3dDByN8pvujTjxjaf0m /home/andy/dev/zig/build/lib/zig/std/os.zig 4850678 1554330534 0 wFvlet_2y9YT6NlFrsqybubwMafHFCIZqq4AxLlcUGuTSZ7VjI8kGmM-3VKPrhkt /home/andy/dev/zig/build/lib/zig/std/io.zig 5112957 1551560716 0 f29y95JhSfGVwEFrutPnquGPPuvvtOLu6_vUUgGbaqn-9uulgFvBnyPU8Zp_sH7r /home/andy/dev/zig/build/lib/zig/std/os/file.zig 5113386 1552707664 0 Mb9m-jj_wRe6yt-TFko9wUGNToAA-IAbVfmR2TYZyPjKP3D87df6BGALjai5uKr2 /home/andy/dev/zig/build/lib/zig/std/os/windows/util.zig 5113248 1551562209 0 F1infSSAdOgbOWl3y2FdUiB-6xrpTaeqUJ5xt_F9IZOeBvvTSLNpkGEg8mrYMcli /home/andy/dev/zig/build/lib/zig/std/os/linux.zig 5113251 1536869149 0 LAlDVvl1TX-3-SB3MOBBjxuEk2Ms8SpkcYBLXnJONgZuo7aV9OYfXLfih9bfGyBS /home/andy/dev/zig/build/lib/zig/std/os/linux/errno.zig 5113278 1551560715 0 TpIIt3DNczfgMKjhDA5mx-4JgoGd7SIV9lXK8GQs9Bix4EmPiL7VlV1oQVwv4gkO /home/andy/dev/zig/build/lib/zig/std/os/linux/x86_64.zig 4849832 1554482295 0 AszHJ9beyMCotgQx4vwYWMBUcRd1dBspsou64fATNYXsSqwB5zAiqVEokfpV5xkF /home/andy/dev/zig/build/lib/zig/std/fmt.zig 4850680 1551559869 0 Polq06O1t-jJ0SobBVqLaQrJVXaKzH4lQj6Na8NRN3Y5N0pZ2I4W2rpwL816MSz- /home/andy/dev/zig/build/lib/zig/std/io/seekable_stream.zig 4850605 1552687041 0 Al_VWoytLHeozYOvM69cCR2Dw1HGYdJc2lDGQDXYoiJy1VuD6GWLIQhxSJuf3Oza /home/andy/dev/zig/build/lib/zig/std/array_list.zig 4877695 1552687041 0 ouDIQ9yX5LXanXk6mc2ypFP-oy2zEt7uLLlPDVUFEfx4AKmeFP1cJWI7YSjYAVt9 /home/andy/dev/zig/build/lib/zig/std/mem.zig 4850649 1551560732 0 rVa_7s1plylcGIUAHEt3eGG8XwSJAEOQzuIaYcnq0KHnb1IoGWUEXUGIW7QHH7fi /home/andy/dev/zig/build/lib/zig/std/elf.zig 4849844 1554305190 0 58Tg88pYb1sgK8SI5_65Gy7lgCqaJmsW2NV-Hi4FeXOK8e2f2T1PofNfdUDgbYit /home/andy/dev/zig/build/lib/zig/std/math.zig 4850647 1536869149 0 9-R2p6FQ_7UIjQMt3N-chgeh-_e_o29Gr2Pybfh2PurmRYT9mIIwsdW9rs6kEJR8 /home/andy/dev/zig/build/lib/zig/std/dwarf.zig 5113295 1552687041 0 6oc1oIiWQJ1OIZGh5KFPtul93GCeAaLFx2VTICI3JMt-Ubnj-vjXvBBclnoo5UQ6 /home/andy/dev/zig/build/lib/zig/std/os/path.zig 4850677 1552707664 0 qom60op_QDptmhKO6SIlpm48SlVybcFOT0gHCjncJvtZhM6uUNqZZ2Q0WV5AsmRP /home/andy/dev/zig/build/lib/zig/std/heap.zig 4850683 1551560704 0 6t3PH8KaWwsz2MElBWZezzyZyeRS-Ttq6k2pJWwRK__NvVQ2hZSu1gk4jA7oDT_Y /home/andy/dev/zig/build/lib/zig/std/linked_list.zig 4850685 1548876783 0 EsROc7EV9MKNd74wRR9tYFhMGs4CL4eqYSPvJVzKTzSoXABBfxVoQwO3N_VivhNG /home/andy/dev/zig/build/lib/zig/std/macho.zig 4850663 1554667129 0 oWDxArJXQLKaeAqd-Uf52hiAjktfqEhJA4XIdlBKCeKOxbYZosChXsfReGM0azCQ /home/andy/dev/zig/build/lib/zig/std/hash_map.zig 4877700 1551559918 0 0SY3kFjpRYGQ1eVmh4UHluGBDFA9LcwANsSlEZLKfBO6hWxoZWcowtXk1SJvSALu /home/andy/dev/zig/build/lib/zig/std/pdb.zig 4850628 1551559906 0 QEww1cNolzNrXqhOMV8uMTXRXQmbekZ7nyUaEqj35x6ZjO8BRVzqvYxQeSmW8c_5 /home/andy/dev/zig/build/lib/zig/std/coff.zig 4877702 1551562259 0 9cFnTGm01DAKqMZim8dzEXoPyN8w3R8UAer53ycBv2PFgg-pM6AsNd6hDf0jlseT /home/andy/dev/zig/build/lib/zig/std/rand.zig 5113402 1554353707 0 m2d1Awh2J4qJuWpBI5sEZJyszBZB2VIZlHvVpXZlt9Vz9JdjXvMN2Cig4ohevME_ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig 4877697 1551560726 0 y2XL97U7jZ9PY4d5TVVZmBzYQc5wrCE_j_utDeU8zfLp5W6iLdFEf_HLbG4-W9Mf /home/andy/dev/zig/build/lib/zig/std/mutex.zig 4877707 1551560708 0 jd03IjTqKqI-sTTbTTL63EjXX2xp9vPInAn4fA0rRis3TeXGgWnP-44_8N0OTRPP /home/andy/dev/zig/build/lib/zig/std/spinlock.zig

The way the cache checking works, is that it calls fstat on all the files in the manifest. For each file, if the inodes and mtimes from the manifest match the results from fstat , then the hash is trusted to be up-to-date for that particular file. Any files with outdated hashes, have their contents hashed, and the manifest file updated with the updated hashes. Finally, all the hashes are hashed together, and that final hash is used as the directory name within zig-cache that Zig looks for previously created build artifacts.

Because Zig uses -MD -MF args to clang when doing @cImport , Zig has a complete list of files that the C code read from, which Zig adds to the cache. So even when using @cImport , Zig's caching system remains perfect. This is a proof of concept for the mechanism that the self-hosted compiler will use to watch and rebuild files.

There is one really fascinating problem to solve when relying on file modification times to determine whether a file's contents have changed. Some file systems, such as macOS's HFS+, do not support nanosecond precision; only seconds. However even nanosecond precision exhibits the problem for very fast systems.

Imagine that a file was modified, and then immediately after, added to the cache. When added to the manifest, the current time and the file's mtime will be the same. If the file is modified again quickly, and then the cache checked, it will still be the same instant in time; the modification to the file did not change the mtime.

There is a beautiful solution to this problem, which is to disqualify a file from being cached if the current time, truncated to the file system time granularity, matches the mtime:

// If the wall clock time, rounded to the same precision as the // mtime, is equal to the mtime, then we cannot rely on this mtime // yet. We will instead save an mtime value that indicates the hash // must be unconditionally computed. static bool is_problematic_timestamp(const OsTimeStamp *fs_clock) { OsTimeStamp wall_clock = os_timestamp_calendar(); // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock. if (fs_clock->nsec == 0) { wall_clock.nsec = 0; if (fs_clock->sec == 0) { wall_clock.sec = 0; } else { wall_clock.sec &= (-1ull) << ctzll(fs_clock->sec); } } else { wall_clock.nsec &= (-1ull) << ctzll(fs_clock->nsec); } return wall_clock.nsec == fs_clock->nsec && wall_clock.sec == fs_clock->sec; }

What's beautiful about this is that it generalizes to any granularity. If a file system had a 1 day mtime granularity, then this would mean any files modified within 1 day would have to have their contents hashed, but files older than that could have their hashes trusted. If a file system always wrote 0 for the mtime, Zig would never trust it.

Thanks to apenwarr's insightful blog post about build system caching for inspiration.

The caching system is disabled by default for the command line interface, except for zig test . zig test does respect --output-dir however, which disables caching. The default caching behavior can be overridden with --cache [on|off] . The Zig Build System enables caching by default.

Caching is always enabled, using the global cache directory, when building libc and compiler-rt. This saves a considerable amount of accumulated time, since they must be available for every compilation.

Zig's compatibility with Valgrind has improved. Notably, Valgrind 3.14 fixes a bug where Zig programs' debug symbols would not be detected. Because of this, the --no-rosegment command line option is removed from Zig. It was only meant to be a temporary workaround.

Thanks to daurnimator for adding a valgrind module to the standard library. This allows Zig code to easily make Valgrind client requests.

In addition to userland API, Zig integrates more tightly with Valgrind. For example, one thing that Zig does in Debug builds is write 0xaa bytes to undefined variables:

undef.zig

const std = @import ( "std" ); pub fn main () void { var array: [ 10 ] u8 = undefined ; std.debug.warn( "array: {x}

" , array); }

$ zig build-exe undef.zig $ ./undef array: aaaaaaaaaaaaaaaaaaaa

Valgrind has detection for branching on undefined values, but if Zig was writing 0xaa values to uninitialized variables, Valgrind would not know that this is supposed to be an undefined canary. It would see the values as defined. However thanks to integrated Valgrind client requests with the Zig language, Valgrind knows what's going on:

undef.zig

const std = @import ( "std" ); pub fn main () void { var x: i32 = 1234 ; std.debug.warn( "x = {}

" , x); x = undefined ; if (x > 10 ) { std.debug.warn( "greater than 10

" ); } }

$ zig build-exe undef.zig $ ./undef x = 1234

When run in valgrind:

==16484== Conditional jump or move depends on uninitialised value(s) ==16484== at 0x224B6C: main (undef.zig:7) ==16484== by 0x22455A: posixCallMainAndExit (bootstrap.zig:86) ==16484== by 0x224350: _start (bootstrap.zig:43)

This is a debugging tool available in Zig that is not available in C - setting things to undefined so that Valgrind can catch it if you accidentally use it.

Language integration with Valgrind is enabled by default in Debug mode, and disabled by default in other modes. The feature can be force enabled or force disabled with --enable-valgrind and --disable-valgrind , respectively. Also available is @import("builtin").valgrind_support which is a comptime bool available for code to find out whether the programmer wants the executable to have Valgrind integration. It's always disabled for compiler_rt and libc/builtin.

The cost of this feature is a few assembly instructions with each assignment to undefined . Only support for Linux, macOS, Solaris, and MinGW on x86_64 is currently implemented.

zig fmt is a tool to format your Zig code to a canonical style. It's implemented in Zig and it's been available as a self-hosted compiler subcommand for a while, but the self-hosted compiler is not done yet and it's not what's available on the download page.

Zig 0.4.0 ships with zig fmt capabilities. Under the hood, it uses zig run and the Build Artifact Caching system, to lazily build zig fmt from source, and then run it. That means even the stage1 C++ Zig compiler is a sort of hybrid - part C++, and part Zig.

Several editors have zig fmt integration available, such as the vim plugin that I use. I believe the VSCode plugin does as well.

There is now has a --check flag, which does everything except modify file contents. That is, it lists non-conforming files on stdout, and then exits with an error if the list is non-empty. #1558 #1555

Other improvements during this release cycle:

Fixed infix operator before multiline string literal.

Fixed argv[0] handled incorrectly.

Removed this as a keyword since it was removed in 0.3.0.

as a keyword since it was removed in 0.3.0. Shritesh Bhattarai fixed zig fmt not detecting extra whitespace at the end of a file. #2074

Shritesh Bhattarai implemented support for documentation comments on parameter declarations.

hryx implemented vertically aligning array literal columns.

hryx implemented Always write a multiline struct literal if a field expr is multiline.

Shritesh Bhattarai fixed first line comment indent in struct init.

hryx implemented allowing one-line for loops.

Shritesh Bhattarai implemented formatting multi line only on trailing comma. (#2184)

Shritesh Bhattarai implemented support for trailing comma after var_args.

There is also a discussion about whether or not to align struct fields, which has some interesting arguments on either side.

I have come up with a plan to migrate translate-c to userland:

Create a C API wrapper for Clang's C++ API. Implement translate-c in Zig with a C API, using the C API wrapper for libclang. The build process for stage1 will link zig.exe without translate-c support, build the translate-c userland library, and then re-link zig.exe with translate-c support.

This will allow us to move thousands of lines of C++ to Zig, and to only have to maintain one implementation of C translation rather than two. This same strategy could be used for other pieces of the Zig project, such as the package manager.

I started this effort with a proof of concept, but there is still a long way to go before this is done.

In addition the following improvements were made:

Much of translated C code looks cleaner thanks to C Pointers.

support no-op cast

--verbose-cimport prints clang command

prints clang command Avoid array concatenation if the init node is empty, for clarity. -Antoine Vugliano

Only detect ints as negative if they are signed. -Antoine Vugliano

Correct array concatenation for incomplete C array initializers. -Antoine Vugliano

Get real child type of array type for incomplete initializers and/or multi-dimensional arrays. -Antoine Vugliano

LemonBoy implemented translation of parameterless C functions (#1978). This solves the only StackOverflow question tagged "Zig".

Antoine Vugliano added support for integer suffixes on 0 (zero) litteral inside macro definitions.

Jimmi Holst Christensen implemented a workaround for #2043.

Jimmi Holst Christensen implemented enough of translate-c to translate the assert macro from glibc.

Jimmi lead this effort. Starting with solving the return type ambiguity (#1628), he then moved on to create zig-spec, update the grammar section of the language reference, and update the stage1 parser to conform.

Thanks to his efforts, Zig syntax now has a formal grammar specification and three independent implementations, despite the fact that the syntax is not fully stablized.

The language reference manual. now has a light theme by default, but respects the user's light/dark preference via the prefers-color-scheme media query. Most browsers don't support this yet, so we just have to wait patiently for the future to arrive.

Instead of a side bar index, the index is inline with the rest of the content. This is simpler and more friendly to all user agents, and means we don't need the media query for mobile devices. It also makes back-references work, so now headers link to the table of contents and the table of contents links to headers.

Both of these things apply to this release notes document as well as the language reference manual.

There is still no HTML documentation available for the Zig Standard Library. See the Roadmap for more details.

The master branch language reference manual is now automatically updated on master branch commits that pass all tests on all platforms.

In addition to the above, the following changes were made to the documentation:

Renamed section keyword to linksection . #1152

keyword to . #1152 Zig no longer has an implicit cast from T to * const T . #1465 removed implicit cast from *T to ?* const T removed implicit cast from T to ?* const T when T is a struct/union

to . #1465 Remove @minValue , @maxValue ; add std.math.minInt and std.math.maxInt . #1466 #1476

, ; add and . #1466 #1476 Now the length expression in array type syntax implies comptime , so function calls, for example, will be evaluated without the need for an explicit comptime block. Similarly: ** and ++ operators force comptime on operands. #1707

, so function calls, for example, will be evaluated without the need for an explicit block. Similarly: for now works with single-item pointers to arrays. #1663 -Jimmi Holst Christensen

Added @bswap builtin function. #767

vegecode added the @bitreverse builtin function. #767

@sizeOf is now defined to be the "ABI size" rather than the "store size". Quoting from the documentation:



This size may contain padding bytes. If there were two consecutive T in memory, this would be the offset in bytes between element at index 0 and the element at index 1. For Integers, consider whether you want to use @sizeOf (T) or @typeInfo (T).Int.bits .



This fixes breaches of the guarantee that @sizeOf (T) >= @alignOf (T) . It also fixes std.mem.secureZero for integers where this guarantee previously was breached, and it fixes std.mem.Allocator for integers where this guarantee previously was breached. #1851 #1864

This size may contain padding bytes. If there were two consecutive T in memory, this would be the offset in bytes between element at index 0 and the element at index 1. For Integers, consider whether you want to use or . This fixes breaches of the guarantee that . It also fixes for integers where this guarantee previously was breached, and it fixes for integers where this guarantee previously was breached. #1851 #1864 emekoi made @enumToInt work on union ( enum ) . #1711

. #1711 Better handling of arrays in packed structs. #677 Allow extern structs to be in packed structs. More helpful error messages when trying to use types in packed structs that are not allowed. Support arrays in packed structs even when they are not byte-aligned. Add compile error for using arrays in packed structs when the padding bits would be problematic. This is necessary since Zig does not have packed arrays.

Better field access of types which have one possible value. #1554 When you do field access of a type which only has one possible value, the result is comptime-known. StorePtr instructions which operate on pointers to types which only have one possible value, the result is a comptime no-op.

@returnAddress and @frameAddress now return a usize rather than a pointer.

rather than a pointer. builtin.TypeInfo.ErrorSet is now ?[]Error instead of struct {errors:[]Error} . #1936

is now instead of . #1936 Removed octal and binary floats from the language. This is technically a breaking change but I would be surprised if anyone was actually using this feature. MateuszOkulus did the documentation updates. #2093

Character literals allow unicode escapes. #2097

Added peer type resolution for * const T and ?*T . #1298

and . #1298 Added compile error for ignoring error. #772

Add allowzero pointer attribute. Only useful for freestanding targets. Also adds safety for @intToPtr when the address is zero. #1953

pointer attribute. Only useful for freestanding targets. Also adds safety for @intToPtr when the address is zero. #1953 alexander implemented switching on bools. #1768

All numbers with comptime known values now implicitly cast to all number types. If the value does not fit, a compile error is emitted.

comptime_ints.zig

const std = @import ( "std" ); const assert = std.debug.assert; test "implicit cast large integer to smaller with comptime value" { const x: u64 = 255 ; const y: u8 = x; assert(y == 255 ); }

$ zig test comptime_ints.zig Test 1/1 implicit cast large integer to smaller with comptime value...OK All tests passed.

#422 #1712

Zig 0.4.0 ships with a new Vector type. This feature is brand new and not well documented yet. Here are some example tests:

vectors.zig

const std = @import ( "std" ); const mem = std.mem; const expect = std.testing.expect; test "vector wrap operators" { const S = struct { fn doTheTest () void { var v: @Vector ( 4 , i32 ) = [ 4 ] i32 { 2147483647 , - 2 , 30 , 40 }; var x: @Vector ( 4 , i32 ) = [ 4 ] i32 { 1 , 2147483647 , 3 , 4 }; expect(mem.eql( i32 , ([ 4 ] i32 )(v +% x), [ 4 ] i32 { - 2147483648 , 2147483645 , 33 , 44 })); expect(mem.eql( i32 , ([ 4 ] i32 )(v -% x), [ 4 ] i32 { 2147483646 , 2147483647 , 27 , 36 })); expect(mem.eql( i32 , ([ 4 ] i32 )(v *% x), [ 4 ] i32 { 2147483647 , 2 , 90 , 160 })); var z: @Vector ( 4 , i32 ) = [ 4 ] i32 { 1 , 2 , 3 , - 2147483648 }; expect(mem.eql( i32 , ([ 4 ] i32 )(-%z), [ 4 ] i32 { - 1 , - 2 , - 3 , - 2147483648 })); } }; S.doTheTest(); comptime S.doTheTest(); } test "vector int operators" { const S = struct { fn doTheTest () void { var v: @Vector ( 4 , i32 ) = [ 4 ] i32 { 10 , 20 , 30 , 40 }; var x: @Vector ( 4 , i32 ) = [ 4 ] i32 { 1 , 2 , 3 , 4 }; expect(mem.eql( i32 , ([ 4 ] i32 )(v + x), [ 4 ] i32 { 11 , 22 , 33 , 44 })); expect(mem.eql( i32 , ([ 4 ] i32 )(v - x), [ 4 ] i32 { 9 , 18 , 27 , 36 })); expect(mem.eql( i32 , ([ 4 ] i32 )(v * x), [ 4 ] i32 { 10 , 40 , 90 , 160 })); expect(mem.eql( i32 , ([ 4 ] i32 )(-v), [ 4 ] i32 { - 10 , - 20 , - 30 , - 40 })); } }; S.doTheTest(); comptime S.doTheTest(); } test "vector float operators" { const S = struct { fn doTheTest () void { var v: @Vector ( 4 , f32 ) = [ 4 ] f32 { 10 , 20 , 30 , 40 }; var x: @Vector ( 4 , f32 ) = [ 4 ] f32 { 1 , 2 , 3 , 4 }; expect(mem.eql( f32 , ([ 4 ] f32 )(v + x), [ 4 ] f32 { 11 , 22 , 33 , 44 })); expect(mem.eql( f32 , ([ 4 ] f32 )(v - x), [ 4 ] f32 { 9 , 18 , 27 , 36 })); expect(mem.eql( f32 , ([ 4 ] f32 )(v * x), [ 4 ] f32 { 10 , 40 , 90 , 160 })); expect(mem.eql( f32 , ([ 4 ] f32 )(-x), [ 4 ] f32 { - 1 , - 2 , - 3 , - 4 })); } }; S.doTheTest(); comptime S.doTheTest(); } test "vector bit operators" { const S = struct { fn doTheTest () void { var v: @Vector ( 4 , u8 ) = [ 4 ] u8 { 0b10101010 , 0b10101010 , 0b10101010 , 0b10101010 }; var x: @Vector ( 4 , u8 ) = [ 4 ] u8 { 0b11110000 , 0b00001111 , 0b10101010 , 0b01010101 }; expect(mem.eql( u8 , ([ 4 ] u8 )(v ^ x), [ 4 ] u8 { 0b01011010 , 0b10100101 , 0b00000000 , 0b11111111 })); expect(mem.eql( u8 , ([ 4 ] u8 )(v | x), [ 4 ] u8 { 0b11111010 , 0b10101111 , 0b10101010 , 0b11111111 })); expect(mem.eql( u8 , ([ 4 ] u8 )(v & x), [ 4 ] u8 { 0b10100000 , 0b00001010 , 0b10101010 , 0b00000000 })); } }; S.doTheTest(); comptime S.doTheTest(); }

$ zig test vectors.zig Test 1/4 vector wrap operators...OK Test 2/4 vector int operators...OK Test 3/4 vector float operators...OK Test 4/4 vector bit operators...OK All tests passed.

Note that Zig of course supports vectors at comptime and likewise all the safety features of arithmetic operators are available for vectors as well.

Vectors provide a way to do Single Instruction Multiple Data in a portable and efficient way. It's much easier for the optimizer to break vectors into scalars than it is to turn scalars into vectors. Even in debug builds which have safety checks, vector operations provide more data throughput than scalar operations.

Thanks to Jimmi Holst Christensen for many contributions to vector support.

To follow progress on vectors and SIMD in Zig, watch issue #903.

Zig's comptime facilities are improved to support reinterpreting memory, as long as the types in question have guaranteed in-memory representation.

bitcast.zig

const builtin = @import ( "builtin" ); const std = @import ( "std" ); const expect = std.testing.expect; test "@bitCast extern structs at runtime and comptime" { const Full = extern struct { number: u16 , }; const TwoHalves = extern struct { half1: u8 , half2: u8 , }; const S = struct { fn doTheTest () void { var full = Full{ .number = 0x1234 }; var two_halves = @bitCast (TwoHalves, full); switch (builtin.endian) { builtin.Endian.Big => { expect(two_halves.half1 == 0x12 ); expect(two_halves.half2 == 0x34 ); }, builtin.Endian.Little => { expect(two_halves.half1 == 0x34 ); expect(two_halves.half2 == 0x12 ); }, } } }; S.doTheTest(); comptime S.doTheTest(); } test "reinterpret bytes of an array into an extern struct" { testReinterpretBytesAsExternStruct(); comptime testReinterpretBytesAsExternStruct(); } fn testReinterpretBytesAsExternStruct () void { var bytes align ( 2 ) = [] u8 { 1 , 2 , 3 , 4 , 5 , 6 }; const S = extern struct { a: u8 , b: u16 , c: u8 , }; var ptr = @ptrCast (* const S, &bytes); var val = ptr.c; expect(val == 5 ); }

$ zig test bitcast.zig Test 1/2 @bitCast extern structs at runtime and comptime...OK Test 2/2 reinterpret bytes of an array into an extern struct...OK All tests passed.

These examples work because extern struct and packed struct have guaranteed in-memory layout.

In addition, as long as the memory is never accessed, pointers with hard coded addresses and pointer arithmetic are supported at comptime :

comptime_ptrs.zig

const std = @import ( "std" ); const expect = std.testing.expect; test "C pointer comparison and arithmetic" { const S = struct { fn doTheTest () void { var one: usize = 1 ; var ptr1: [*c] u32 = 0 ; var ptr2 = ptr1 + 10 ; expect(ptr1 == 0 ); expect(ptr1 >= 0 ); expect(ptr1 <= 0 ); expect(ptr1 < 1 ); expect(ptr1 < one); expect( 1 > ptr1); expect(one > ptr1); expect(ptr1 < ptr2); expect(ptr2 > ptr1); expect(ptr2 >= 40 ); expect(ptr2 == 40 ); expect(ptr2 <= 40 ); ptr2 -= 10 ; expect(ptr1 == ptr2); } }; S.doTheTest(); comptime S.doTheTest(); }

$ zig test comptime_ptrs.zig Test 1/1 C pointer comparison and arithmetic...OK All tests passed.

A variable may be specified to be a thread-local variable using the threadlocal keyword:

tls.zig

const std = @import ( "std" ); const assert = std.debug.assert; threadlocal var x: i32 = 1234 ; test "thread local storage" { const thread1 = try std.os.spawnThread({}, testTls); const thread2 = try std.os.spawnThread({}, testTls); testTls({}); thread1.wait(); thread2.wait(); } fn testTls (context: void ) void { assert(x == 1234 ); x += 1 ; assert(x == 1235 ); }

$ zig test tls.zig Test 1/1 thread local storage...OK All tests passed.

For Single-Threaded Builds, all thread local variables are treated as Global Variables.

Thread local variables may not be const .

Note that thread local variables have better performance for non- position independent code, which is one reason for Static by Default.

Thanks to Rich Felker from the musl libc project, who gave me this crucial information, when I was implementing thread local storage for static builds:

to satisfy the abi, your init code has to write the same value to that memory location as the value passed to the [arch_prctl] syscall

This deletes some legacy cruft, and produces leaner object files. Example:

var x: i32 = 1234 ; export fn entry () i32 { return x; }

This produced:

@x = internal unnamed_addr global i32 1234, align 4 @0 = internal unnamed_addr constant i32* @x, align 8

and @0 was never even used. After Zig 0.4.0, @0 is not produced.

This fixes a bug: Zig was creating invalid LLVM IR when one of these globals that shouldn't exist takes the address of a thread local variable. In LLVM 8, it would produce a linker error. But probably after my bug report is solved it will be caught by the IR verifier.

Thanks to Rui Ueyama and George Rimar who helped me troubleshoot this problem when I filed an LLVM bug report.

One of the use cases of Zig is to beat C at its own game, and this means being a better language for using C libraries than C is. It's a tall order to fulfill, and it was compromised by Zig's pointer reform. An unfortunate side effect of having more type safety in pointer types was that C's very unsafe pointer types became awkward to use. It is ambiguous whether pointers should be translated as single-item pointers ( *T ) or unknown-length pointers ( [*]T ).

C Pointers are a compromise so that Zig code can utilize translated header files directly.

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.

[*c]T - C pointer.

Supports all the syntax of the other two pointer types.

Implicitly casts to other pointer types, as well as Optional Pointers. When a C pointer is implicitly casted to a non-optional pointer, safety-checked Undefined Behavior occurs if the address is 0.

Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked Undefined Behavior. Optional C pointers introduce another bit to keep track of null, just like ? usize . Note that creating an optional C pointer is unnecessary as one can use normal Optional Pointers.

. Note that creating an optional C pointer is unnecessary as one can use normal Optional Pointers. Supports implicit casting to and from integers.

Supports comparison with integers.

Does not support Zig-only pointer attributes such as alignment. Use normal Pointers please!

It's planned to support `if`, `orelse`, `null`, and `.?` for C pointers.

While C pointers themselves have all the footguns associated with, well, C pointers, there is safety when converting to Zig pointers:

test.zig

test "cast null C pointer to Zig pointer" { var c_ptr: [*c] i32 = 0 ; var zig_ptr: * i32 = c_ptr; }

$ zig test test.zig Test 1/1 cast null C pointer to Zig pointer...cast causes pointer to be null /home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:3:25 : 0x2040f1 in ??? (test) var zig_ptr: *i32 = c_ptr; ^ /home/andy/dev/zig/build/lib/zig/std/special/test_runner.zig:13:25 : 0x225bdb in ??? (test) if (test_fn.func()) |_| { ^ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig:122:22 : 0x225366 in ??? (test) root.main() catch |err| { ^ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig:43:5 : 0x2250d1 in ??? (test) @noInlineCall(posixCallMainAndExit); ^ Tests failed. Use the following command to reproduce the failure: /home/andy/dev/www.ziglang.org/docgen_tmp/test

In Zig 0.3.0, @typeOf(@import("builtin")) is (import) , which was a special "namespace" type. In Zig 0.4.0, @typeOf(@import("builtin")) is type . The special "namespace" type is removed; instead all files are structs with no fields.

This makes the language smaller and simpler, and makes imported files easier to inspect with reflection, since they are structs.

With this change, struct types get fully qualified names and function symbol names become fully qualified. This means that when using zig test it's no longer necessary to manually namespace test names as they will be automatically namespaced.

There is a new compile error for importing a file outside the package path. The new CLI option --main-pkg-path can be used to choose a different root package directory besides the one inferred from the root source file. The corresponding Zig Build System API is artifact.setMainPkgPath(path) .

Zig has a new type to make dealing with enums more attractive. It can be thought of as the enum equivalent of comptime_int . The enum equivalent of a number literal.

enum-lit.zig

const std = @import ( "std" ); pub fn main () void { print( 1234 , .hex); print( 1234 , .normal); } fn print (x: i32 , mode: enum { normal, hex }) void { switch (mode) { .normal => std.debug.warn( "{}

" , x), .hex => std.debug.warn( "0x{x}

" , x), } }

$ zig build-exe enum-lit.zig $ ./enum-lit 0x4d2 1234

Here you can see an anonymous enum was used rather than an opaque bool parameter. The idea here is to make it so easy to have meaningful values, that programmers will want to use them, even if they're feeling lazy.

The ergonomics of this feature feel great. Here's tgschultz updating some of the standard library API to use anonymous enum literals.

Note that this feature is checked by the compiler. It's completely safe:

test.zig

const std = @import ( "std" ); test "invalid enum literal" { print( 1234 , .hex); print( 1234 , .invalid); } fn print (x: i32 , mode: enum { normal, hex }) void { switch (mode) { .normal => std.debug.warn( "{}

" , x), .hex => std.debug.warn( "0x{x}

" , x), } }

$ zig test test.zig /home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:5:17: error: enum 'enum:8:24' has no field named 'invalid' print(1234, .invalid); ^ /home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:8:24: note: 'enum:8:24' declared here fn print(x: i32, mode: enum { normal, hex }) void { ^

std.debug.failing_allocator is now a *Allocator rather than the FailingAllocator state. -Jimmi Holst Christensen

is now a rather than the state. -Jimmi Holst Christensen Renamed std.event.tcp to std.event.net .

to . update std lib API for I/O Renamed std.io.FileInStream to std.os.File.InStream Renamed std.io.FileInStream.init(file) to file.inStream() Renamed std.io.FileOutStream to std.os.File.OutStream Renamed std.io.FileOutStream.init(file) to file.outStream() Fewer error code possibilities from several os functions Renamed std.event.net.socketRead to std.event.net.read Renamed std.event.net.socketWrite to std.event.net.write Added std.event.net.readv Added std.event.net.writev Added std.event.net.readvPosix Added std.event.net.writevPosix Added std.event.net.OutStream Added std.event.net.InStream Added std.event.io.InStream Added std.event.io.OutStream

std lib: posixRead can return less than buffer size. #1414 std.io.InStream.read now can return less than buffer size introduce std.io.InStream.readFull for previous behavior add std.os.File.openWriteNoClobberC rename std.os.deleteFileWindows to std.os.deleteFileW remove std.os.deleteFilePosix add std.os.deleteFileC std.os.copyFile no longer takes an allocator std.os.copyFileMode no longer takes an allocator std.os.AtomicFile no longer takes an allocator add std.os.renameW add windows support for std.os.renameC

add std.os.linux.vfork and std.os.linux.exit_group

and changed std.os.time.sleep(seconds: usize , nanoseconds: usize ) to (nanoseconds: u64 ) . -Marc Tiehuis

to . -Marc Tiehuis Added std.math.maxInt and std.math.minInt

and Added std.crypto.HmacBlake2s256

Added std.net.Address.port -Josh Wolfe

-Josh Wolfe Added std.io.NullOutStream and std.io.CountingOutStream -Jimmi Holst Christensen

and -Jimmi Holst Christensen std.io.InStream(E).readStruct returns a value instead of taking a pointer. -Jimmi Holst Christensen

returns a value instead of taking a pointer. -Jimmi Holst Christensen Added std.atomic.Int.set . -Josh Wolfe

. -Josh Wolfe Added std.meta.intToEnum .

. Revamped the std.rand.Rand API to better handle the concept of bias. -Josh Wolfe Added std.rand.Rand.uintLessThanBiased Added std.rand.Rand.uintAtMostBiased Added std.rand.Rand.intRangeLessThanBiased Added std.rand.Rand.intRangeAtMostBiased

API to better handle the concept of bias. -Josh Wolfe Added std.HashMap.getOrPutValue . -Jimmi Holst Christensen

. -Jimmi Holst Christensen Jimmi Holst Christensen revamped std.io.readLine std.io.readLine has a new prototype Added std.io.readLineFrom Added std.io.readLineSlice Added std.io.readLineSliceFrom

Added std.io.SeekableStream . Dwarf debug info is modified to use this instead of std.os.File directly to make it easier for bare metal projects to take advantage of debug info parsing.

. Dwarf debug info is modified to use this instead of directly to make it easier for bare metal projects to take advantage of debug info parsing. Added std.debug.StackIterator .

. rework the readInt/writeInt functions: io.InStream().readIntNe renamed to readIntNative io.InStream().readIntLe renamed to readIntLittle io.InStream().readIntBe renamed to readIntBig introduced io.InStream().readIntForeign io.InStream().readInt has parameter order changed io.InStream().readVarInt has parameter order changed io.InStream().writeIntNe renamed to writeIntNative introduced io.InStream().writeIntForeign io.InStream().writeIntLe renamed to writeIntLittle io.InStream().writeIntBe renamed to writeIntBig io.InStream().writeInt has parameter order changed mem.readInt has different parameters and semantics introduced mem.readIntNative introduced mem.readIntForeign mem.readIntBE renamed to mem.readIntBig and different API mem.readIntLE renamed to mem.readIntLittle and different API introduced mem.readIntSliceNative introduced mem.readIntSliceForeign introduced mem.readIntSliceLittle introduced mem.readIntSliceBig introduced mem.readIntSlice mem.writeInt has different parameters and semantics introduced mem.writeIntNative introduced mem.writeIntForeign mem.writeIntBE renamed to mem.readIntBig and different semantics mem.writeIntLE renamed to mem.readIntLittle and different semantics introduced mem.writeIntSliceForeign introduced mem.writeIntSliceNative introduced mem.writeIntSliceBig introduced mem.writeIntSliceLittle introduced mem.writeIntSlice removed mem.endianSwapIfLe removed mem.endianSwapIfBe removed mem.endianSwapIf added mem.littleToNative added mem.bigToNative added mem.toNative added mem.nativeTo added mem.nativeToLittle added mem.nativeToBig

Add error .DeviceBusy as a possible result of std.os.posixOpen . I observed EBUSY when trying to open for writing a tty fd that is already opened with screen.

as a possible result of . I observed when trying to open for writing a tty fd that is already opened with screen. std.debug.assert no longer has special behavior for test builds. Use std.testing.expect to detect test failures in a way that will work in release builds. See assert vs expect.

no longer has special behavior for test builds. Use to detect test failures in a way that will work in release builds. See assert vs expect. std.mem.Allocator.create is removed. std.mem.Allocator.createOne is renamed to std.mem.Allocator.create . The problem with the previous API is that even after copy elision, the initalization value passed as a parameter would always be a copy. With the new API, once copy elision is done, initialization functions can directly initialize allocated memory in place. #1872 #1873

is removed. is renamed to . The problem with the previous API is that even after copy elision, the initalization value passed as a parameter would always be a copy. With the new API, once copy elision is done, initialization functions can directly initialize allocated memory in place. #1872 #1873 std.mem.split is removed. std.mem.separate and std.mem.tokenize are added.

is removed. and are added. Marc Tiehuis added std.fmt.parseFloat . This is not intended to be the long-term implementation as it doesn't provide various properties that we eventually will want (e.g. round-tripping, denormal support). It also uses f64 internally so the wider f128 will be inaccurate. See #2207 for robust float parsing in the Zig Standard Library.

. This is not intended to be the long-term implementation as it doesn't provide various properties that we eventually will want (e.g. round-tripping, denormal support). It also uses internally so the wider will be inaccurate. See #2207 for robust float parsing in the Zig Standard Library. John Schmidt added std.PriorityQueue .

. Added std.os.posixMProtect .

. std.os.changeCurDir no longer has an allocator parameter.

no longer has an allocator parameter. Shawn Landden added std.ascii . Rohlem added test coverage and fixed the logic.

. Rohlem added test coverage and fixed the logic. Shawn Landden added std.math.mulWide .

. kristopher tate exposed std.crypto.HmacBlake2s256 .

. Wink Saville added SegmentedList.shrink .

. tgschultz added serialization and deserialization abstractions: Added std.io.Serializer . Added std.io.Deserializer . Added std.io.BitInStream . Added std.io.BitOutStream . Added std.meta.TagPayloadType . Added std.meta.trait.isUnsignedInt . Added std.meta.trait.isSignedInt .

daurnimator added std.meta.stringToEnum

kristopher tate introduced the concept of std.mem.separate vs std.mem.tokenize .

vs . daurnimator added std.math.IntFittingRange .

. daurnimator added std.LinkedList.concat .

. tgschultz added std.meta - helper functions for doing reflection. #1662

Previously, std.debug.assert would @panic in test builds, if the assertion failed. Now, it's always unreachable .

This makes release mode test builds more accurately test the actual code that will be run.

However this requires tests to call std.testing.expect rather than std.debug.assert to make sure output is correct.

Here is the explanation of when to use either one, copied from the assert doc comments:

Inside a test block, it is best to use the std.testing module rather than assert, because assert may not detect a test failure in ReleaseFast and ReleaseSmall modes. Outside of a test block, assert is the correct function to use.

See #1304.

Zig code can use std.debug.captureStackTrace at any time for debugging purposes:

stack-traces.zig

const std = @import ( "std" ); const builtin = @import ( "builtin" ); var address_buffer: [ 8 ] usize = undefined ; var trace1 = builtin.StackTrace{ .instruction_addresses = address_buffer[ 0 .. 4 ], .index = 0 , }; var trace2 = builtin.StackTrace{ .instruction_addresses = address_buffer[ 4 ..], .index = 0 , }; pub fn main () void { foo(); bar(); std.debug.warn( "first one:

" ); std.debug.dumpStackTrace(trace1); std.debug.warn( "



second one:

" ); std.debug.dumpStackTrace(trace2); } fn foo () void { std.debug.captureStackTrace( null , &trace1); } fn bar () void { std.debug.captureStackTrace( null , &trace2); }

$ zig build-exe stack-traces.zig $ ./stack-traces first one: /home/andy/dev/www.ziglang.org/docgen_tmp/stack-traces.zig:27:32 : 0x22586d in ??? (stack-traces) std.debug.captureStackTrace(null, &trace1); ^ /home/andy/dev/www.ziglang.org/docgen_tmp/stack-traces.zig:17:8 : 0x225819 in ??? (stack-traces) foo(); ^ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig:112:22 : 0x22528b in ??? (stack-traces) root.main(); ^ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig:43:5 : 0x225081 in ??? (stack-traces) @noInlineCall(posixCallMainAndExit); ^ second one: /home/andy/dev/www.ziglang.org/docgen_tmp/stack-traces.zig:31:32 : 0x22588d in ??? (stack-traces) std.debug.captureStackTrace(null, &trace2); ^ /home/andy/dev/www.ziglang.org/docgen_tmp/stack-traces.zig:18:8 : 0x22581e in ??? (stack-traces) bar(); ^ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig:112:22 : 0x22528b in ??? (stack-traces) root.main(); ^ /home/andy/dev/zig/build/lib/zig/std/special/bootstrap.zig:43:5 : 0x225081 in ??? (stack-traces) @noInlineCall(posixCallMainAndExit); ^

This feature is used in the GeneralPurposeDebugAllocator project for helping detect and diagnose memory issues.

Zig 0.4.0 brings breaking changes to the std.mem.Allocator interface and API.

Before, allocator implementations had to provide allocFn , reallocFn , and freeFn .

Now, they must provide only reallocFn and shrinkFn . Reallocating from a zero length slice is allocation, and shrinking to a zero length slice is freeing.

When the new memory size is less than or equal to the previous allocation size, reallocFn now has the option to return error.OutOfMemory to indicate that the allocator would not be able to take advantage of the new size.

For more details see #1306.

I have reproduced the documentation comments here:

pub const Allocator = struct { pub const Error = error {OutOfMemory}; reallocFn: fn ( self: *Allocator, old_mem: [] u8 , old_alignment: u29 , new_byte_count: usize , new_alignment: u29 , ) Error![] u8 , shrinkFn: fn ( self: *Allocator, old_mem: [] u8 , old_alignment: u29 , new_byte_count: usize , new_alignment: u29 , ) [] u8 , };

This allows allocators and data structures to "negotiate" with each other. For example, here is std.ArrayList.shrink :

pub fn shrink (self: *Self, new_len: usize ) void { assert(new_len <= self.len); self.len = new_len; self.items = self.allocator.realloc(self.items, new_len) catch |e| switch (e) { error .OutOfMemory => return , }; }

I am pleased to announce our newest Zig team member, Antoine Vugliano.

He has shown continued dedication and discipline in his contributions to the Zig programming language project. The quality of his work speaks for itself.

I look forward to working with Antoine as we continue to push Zig toward 1.0.0 and beyond.

Thanks to Jeff Fowler for creating sublime-zig-language and for creating the pull request to GitHub to add support for Zig.

Finally, GitHub decided that Zig is popular enough to gain official recognition.

As a fun last step, Marc Tiehuis marked third-party dependencies as vendored, so that we can have this pretty language bar on GitHub:

Now you can discover Zig projects on GitHub via their "trending" web page.

Function type mismatches now have an error note which explains in more detail why the function types were incompatible:

test.zig

fn do_the_thing (func: fn (arg: i32 ) void ) void {} fn bar (arg: bool ) void {} test "fn types" { do_the_thing(bar); }

$ zig test test.zig /home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void' do_the_thing(bar); ^ /home/andy/dev/www.ziglang.org/docgen_tmp/test.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32' do_the_thing(bar); ^

Antoine Vugliano fixed --emit asm on windows. #1612

Jimmi Holst Christensen fixed StackTrace not being resolved when panic is invalid (#1615)

Fixed missing .h files for @cImport. #1634

No more blank lines printed after linker errors. Fixed by disabling the error limit when invoking LLD.

Fixed code still using old ptr syntax. -Jimmi Holst Christensen

Fixed stack pointer register name on i386

Removed implicit cast from number literal to enum

limit integer types to maximum bit width of 65535. #1541

Fixed .h file locating on macos 10.14

fix assertion failure related to @intToEnum

std/rand: fix ziggurat next_f64 call. -Marc Tiehuis

fix @intCast not catching negative numbers to unsigned

better file path handling in stage1 compiler. #1693 #1746

Antoine Vugliano fixed stack traces on windows.

Josh Wolfe fixed child_process piped streams not getting closed

Fixed intToPtr to fn type when the address is hardcoded (#1842) -Jimmi Holst Christensen

vegecode fixed mutate through ptr initialized with constant intToPtr value. #1171

Jimmi Holst Christensen fixed @typeInfo returning incorrect results sometimes.

Jimmi Holst Christensen fixed using mem.writeInt at comptime can change the type of a structs field. #1866

Fixed debug info for function pointers.

Fixed @typeInfo returning incorrect function return type sometimes.

Fixed incorrect parameter names for std.math.atan2 .

. Sauhnvour improved the line number accuracy of stack traces on Windows. #1503

krisopher tate fixed @compileLog with a pointer to an opaque value.

Fixed incorrect timeval struct type on macOS. #1648

Matthew McAllister fixed runtime assignment to comptime aggregate field.

Matthew McAllister fixed slice concatenation.

Fixed using the result of @intCast to u0. #1817

Fixed compiler assertion failure when returning value from test. #1935

@truncate now is comptime -known to be 0 when destination integer type has 0 bits. If the destination type is a comptime_int , treat it as an Implicit Cast. #1568

-known to be 0 when destination integer type has 0 bits. If the destination type is a , treat it as an Implicit Cast. #1568 Add missing compile error for OpaqueType inside structs/unions. #1862

Fix mmap syscalls invoking undefined behavior when address 0 is used. Found thanks to Zig's new safety checks for null pointers.

Typecheck the panic function. The prototype of panic is added to @import ( "builtin" ) and then used to do an implicit cast of the panic function to this prototype, rather than redoing all the implicit cast logic. #1894 #1895

and then used to do an implicit cast of the panic function to this prototype, rather than redoing all the implicit cast logic. #1894 #1895 emekoi fixed the secret debug safety union tag incorrectly being included in release modes.

Matthew McAllister fixed lvalue dereference type checking. Previously, if a dereference instruction was an lvalue, it would fail to typecheck that the value being dereferenced was indeed a pointer.

sjdh02 fixed BufferedInStream not reading delayed input.

Benoit Jauvin-Girard fixed std.math.powi so powi(x, +-0) = 1 for any x.

LemonBoy prevented crashing in tagged enums rendering (#1986)

Fix @bitCast when src/dest types have mismatched handle_is_ptr . #991 #1934

. #991 #1934 Fixed incorrectly trying to memset at comptime. #718

Correctly handle the case when there are multiple externs and an export in the same object, and they all share the same name. #529

Better error message on Windows when a file name contains an asterisk.

Fixed .h file generation not respecting @export.

Fixed @typeName on slices. #2026

Jimmi Holst Christensen fixed function taking TypeInfo and returning type crashed compiler. #1600

Added test for spawning child process with empty environment. Thanks to BenoitJGirard for pointing out the child process implementation needs 3 extra null bytes in #2031.

LemonBoy fixed generation of comptime slices.

Fixed @setRuntimeSafety not able to override release modes.

Fixed while continue block not checking for ignored expression. #957

Added compile error for wrong type with use . #1557

. #1557 Antoine Vugliano improved Zig's PDB (Windows) debug info parsing and used it to fix Zig's C ABI support for Windows. The C ABI tests are now passing on all Tier 1 Support targets.

Fixed global assembly parsing. Previously, global assembly was parsed expecting it to have the template syntax. However global assembly has no inputs, outputs, or clobbers, and thus does not have template syntax. This is now fixed.

Fixed parsing of large hex float literals. Float literals now parse using musl's 128 bit float code. Fixes float literals not having 128 bit precision. #2083

Marc Tiehuis fixed bigint_append_buf . All current usages had base 10 and a limb length of 2, hence why we weren't hitting this error in practice.

. All current usages had base 10 and a limb length of 2, hence why we weren't hitting this error in practice. Wink Saville fixed std.json.pushToParent to work for arrays of Objects

to work for arrays of Objects Shawn Landden fixed ELF auxv handling.

Duncan fixed the setsockopt syscall on linux.

Michael Dusan fixed zig run to accept executable args.



The -- double-hyphen is now used to end further zig processing of command line options. All arguments after -- will be passed on to the executable. eg. --help will be passed on:



zig run foo.zig -- --help



#2148

The double-hyphen is now used to end further zig processing of command line options. All arguments after will be passed on to the executable. eg. will be passed on: #2148 emekoi added a missing compile error for implicit cast from * const T to *[ 1 ]T based on a patch from kristopher tate.

to based on a patch from kristopher tate. Fixed @divFloor returning incorrect value. #2152

Fixed NaN comparing equal to itself. This was broken both in comptime code and in runtime code. #1174

Fixed dereferencing a zero bit type. Before only u0 was special cased in handling a dereference; now all zero bit types are handled the same way. Dereferencing a pointer to a zero bit type always gives a comptime-known result.

Jimmi Holst Christensen fixed inability to capture pointer to elements of const array in for loop. #1726

loop. #1726 Ryan Liptak fixed std.HashMap.remove returning invalid memory.



Now returns a copy of the removed kv instead of a pointer to the removed kv. The removed kv gets overwritten when shifting the hash map after the removal, so returning a pointer to it will have another kv's values in it after the return.



This bug had some nasty downstream effects in things like std.BufSet and std.BufMap where delete would free a still in-use KV and leave the actually removed KV un-free'd.

returning invalid memory. Now returns a copy of the removed kv instead of a pointer to the removed kv. The removed kv gets overwritten when shifting the hash map after the removal, so returning a pointer to it will have another kv's values in it after the return. This bug had some nasty downstream effects in things like and where delete would free a still in-use KV and leave the actually removed KV un-free'd. Ryan Saunderson fixed detecting the latest installed version of the Windows SDK and related tests. #1665

alexander added DIFlagStaticMember flag to functions. This prevents LLVM from generating debug info for struct member functions with a pointer as the first parameter as though the first parameter were the implicit "this" pointer from C++.

Quetzal Bradley fixed a compile error in std.os.File.openWriteNoClobber and added test coverage.

and added test coverage. sjdh02 fixed check for 64-bit arm platforms with new targets.

Zig has known bugs.

The first release that will ship with no known bugs will be 1.0.0.

Here is the roadmap from 0.3.0 release notes:

Redo coroutines without using LLVM Coroutines and rework the semantics. See #1363 and #1194.

Tuples instead of var args. #208

Well-defined copy-eliding semantics. #287

Self-hosted compiler. #89

Get to 100% documentation coverage of the language

Auto generated documentation. #21

Package manager. #943

I am sad to report that this list was too ambitious for one release cycle, and while there has been considerable progress, none of these items are complete. And so the roadmap for 0.4.0 is the same.

It's no longer planned for Zig to have Type Based Alias Analysis. However, there are some open research topics on potentially outlawing all kinds of aliasing unless explicitly declared. Subscribe to #1108 for more details.

For now, Zig has a well-defined memory model, and aliasing is always allowed. Note, however, that not all types have a guaranteed in-memory layout.

Linking macOS/COFF files is still in a sad state. The Mach-O code in LLD, Zig's linker, has been barely maintained for several years now. Zig has a fork of LLD in its source tree with a hacky patch to fix linking "Hello World" on macOS.

It's now planned for the Zig project to have its own linker. This is in part due to the lack of Mach-O maintenance, and in part because LLD has no plans to do incremental linking - a feature that I foresee to be necessary to achieve the performance we want to have for large projects in the self-hosted compiler.

For now the self-hosted compiler uses --system-linker-hack to be able to link successfully, which is a compromise of Zig's promise that it can build on any target, for any target.

There is also a workaround in the linker phase of Zig for compiler-rt.a and builtin.a. I had to make them object files rather than archive files to avoid crashing LLD.

There has been no progress on the self-hosted compiler in this release cycle. Progress is blocked on the volume of core language changes still happening, as well as no-copy semantics, and Reworking Coroutines.

The package manager will be one of the focus areas of the 0.5.0 release cycle. It depends on networking, which depends on Reworking Coroutines, which depends on no-copy semantics.

There is no progress on coroutines in this release cycle. In fact there has been anti-progress.

Zig 0.4.0 introduces a memory leak to all coroutines. There is an issue where a coroutine calls the function and it frees its own stack frame, but then the return value of shrinkFn is a slice, which is implemented as an sret struct. Writing to the return pointer causes invalid memory write. We could work around it by having a global helper function which has a void return type and calling that instead. But instead this hack will suffice until I rework coroutines to be non-allocating. Basically coroutines are not supported right now until they are reworked as in #1194.

Reworking coroutines is to be a major focus of 0.5.0 as it is blocking networking, the Package Manager, and the Self-Hosted Compiler.

Here is a small selection of interesting proposals that have been accepted, to give you an idea of the upcoming changes to Zig.

Full list of accepted proposals

GeneralPurposeDebugAllocator - work in progress general purpose debug allocator with livestreamed development.

zig-benchmark - Small and easy micro-benchmarking library.

Oxid - an arcade-style game where you fight waves of monsters in a fixed-screen maze.

trOS - tiny aarch64 baremetal OS thingy.

zig-clap - Simple command line argument parsing library

Simple Amplifier - a very simple example of LV2 plugin built in Zig.

benchmark.unicode - A command-line tool written in Zig to measure the performance of various UTF8 decoders. The decoders are written in Zig or C.

hexdump-zip - produce an annotated hexdump of a zipfile

Special thanks to those who donate monthly. Thanks to you, Zig is not driven by the needs of a business; instead it exists solely to serve the open source community.