Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
Backed by the Zig Software Foundation, the project is financially sustainable. These core team members work on Zig full-time:
Please consider a recurring donation to the ZSF to help us pay more contributors!
This release features 10 months of work: changes from 272 different contributors, spread among 4737 commits. It is the début of the Self-Hosted Compiler.
I am pleased to announce our newest Zig team member, Cody Tapscott (screenname: topolarity).
Cody excels at troubleshooting and debugging. He fearlessly deep dives into unfamiliar areas of code, relentlessly attacking the problem until a solution presents itself.
In addition, Cody has proven to be a steadfast community leader, setting an example for how to treat others with kindness and respect.
Please give him a warm welcome to the core Zig team.
If you have been following along with Zig's development process lately, you might have noticed a distinct uptick in pull requests being merged, Bug Fixes to the Self-Hosted Compiler, and implementation of Language Changes.
Vexu has been doing brilliant work, and we are all fortunate that, one, he has decided to take on full-time hours lately, and two, donations have been steadily rising so that there are enough funds to offer the hours.
A green check mark (✅) indicates the target meets all the requirements for the support tier. The other icons indicate what is preventing the target from reaching the support tier. In other words, the icons are to-do items. If you find any wrong data here please submit a pull request!
freestanding | Linux 3.16+ | macOS 11+ | Windows 8.1+ | WASI | |
---|---|---|---|---|---|
x86_64 | ✅ | ✅ | ✅ | ✅ | N/A |
x86 | ✅ | #1929 🐛📦 | 💀 | #537 🐛📦 | N/A |
aarch64 | ✅ | #2443 🐛 | ✅ | 🐛📦🧪 | N/A |
arm | ✅ | #3174 🐛📦 | 💀 | 🐛📦🧪 | N/A |
mips | ✅ | #3345 🐛📦 | N/A | N/A | N/A |
riscv64 | ✅ | #4456 🐛📦 | N/A | N/A | N/A |
sparc64 | ✅ | #4931 🐛📦🧪 | N/A | N/A | N/A |
powerpc64 | ✅ | 🐛📦 | N/A | N/A | N/A |
wasm32 | ✅ | N/A | N/A | N/A | ✅ |
free standing | Linux 3.16+ | macOS 11+ | Windows 8.1+ | FreeBSD 12.0+ | NetBSD 8.0+ | DragonFlyBSD 5.8+ | UEFI | |
---|---|---|---|---|---|---|---|---|
x86_64 | Tier 1 | Tier 1 | Tier 1 | Tier 1 | ✅ | ✅ | ✅ | ✅ |
x86 | Tier 1 | ✅ | 💀 | ✅ | 🔍 | 🔍 | N/A | ✅ |
aarch64 | Tier 1 | ✅ | Tier 1 | 🔍 | 🔍 | 🔍 | N/A | 🔍 |
arm | Tier 1 | ✅ | 💀 | 🔍 | 🔍 | 🔍 | N/A | 🔍 |
mips64 | ✅ | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | N/A |
mips | Tier 1 | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | N/A |
powerpc64 | ✅ | ✅ | 💀 | N/A | 🔍 | 🔍 | N/A | N/A |
powerpc | ✅ | ✅ | 💀 | N/A | 🔍 | 🔍 | N/A | N/A |
riscv64 | Tier 1 | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | 🔍 |
sparc64 | Tier 1 | ✅ | N/A | N/A | 🔍 | 🔍 | N/A | N/A |
zig targets
is guaranteed to include this target.freestanding | Linux 3.16+ | Windows 8.1+ | FreeBSD 12.0+ | NetBSD 8.0+ | UEFI | |
---|---|---|---|---|---|---|
x86_64 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 |
x86 | Tier 1 | Tier 2 | Tier 2 | ✅ | ✅ | Tier 2 |
aarch64 | Tier 1 | Tier 2 | ✅ | ✅ | ✅ | ✅ |
arm | Tier 1 | Tier 2 | ✅ | ✅ | ✅ | ✅ |
mips64 | Tier 2 | Tier 2 | N/A | ✅ | ✅ | N/A |
mips | Tier 1 | Tier 2 | N/A | ✅ | ✅ | N/A |
riscv64 | Tier 1 | Tier 2 | N/A | ✅ | ✅ | ✅ |
powerpc32 | Tier 2 | Tier 2 | N/A | ✅ | ✅ | N/A |
powerpc64 | Tier 2 | Tier 2 | N/A | ✅ | ✅ | N/A |
bpf | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
hexagon | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
amdgcn | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
sparc | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
s390x | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
lanai | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
csky | ✅ | ✅ | N/A | ✅ | ✅ | N/A |
freestanding | emscripten | |
---|---|---|
wasm32 | Tier 1 | ✅ |
zig targets
will display the target if it is available.-femit-asm
and cannot emit
object files, in which case -fno-emit-bin
is enabled by
default and cannot be overridden.Tier 4 targets:
During this release cycle, a lot of time was spent on both visible and invisible improvements. Refactors were done to increase readability, maintainability, and also the performance of the backend and its generated code. With this release, we're one step closer to being able to make the WebAssembly Backend the default for debug mode.
Resolved issues:
crypto.25519.field.Fe.isSquare
causes too many locals to be emitted (#11947)Improvements to the Standard Library:
__muloti4
from libc++ (#10719)
zig test
has proper detection for Windows console.argv
on WindowsHKEY
to *opaque {}
The 32-bit Windows tarball is missing for this release due to #12886. It will be available again in the 0.10.1 bugfix release.
Previously, updating the SYS
enum for each architecture required
manually looking at the syscall tables and inserting any new additions.
Now there is a tool, generate_linux_syscalls.zig
, that automates
this process using the syscall tables in the Linux source tree. On
architectures without a table, it runs zig cc as a pre-processor to
extract the system-call numbers from the Linux headers.
Improvements to the Standard Library:
Added support for macOS 13 (Ventura); dropped support for macOS 10 (Catalina). This matches Apple's security policy.
Prior to this change Zig would assume the ABI for Apple targets to be GNU
which could result in subtle errors of emitting calls to non-existent
system libc provided functions such as _sincosf
which is a GNU
extension and therefore not provided by macOS. This would result in linker
errors due to not finding the symbol in libSystem.tbd
.
With this change, Zig now correctly identifies macOS (and other Apple platforms such as
iPhoneOS, tvOS, and watchOS) as having ABI none
(#11684).
In LLVM, this translates to unknown
:
// main.ll
target triple = "aarch64-unknown-macos-unknown"
stat
struct definition for sparc64.powerpc64 moved from Tier 3 Support to Tier 2 Support.
powerpc64le now has CI test coverage for the behavior tests, the Standard Library, targeting musl, and targeting glibc.
Softfloat support was added in compiler-rt.
Zig can now build itself for the WASI target.
The chmod
is because of
WebAssembly/wasi-filesystem#33.
This snippet demonstrates creating a WASI build of Zig, then executing it with wasmtime 1.0.1, cross compiling an executable for x86_64-linux, and then executing the cross-compiled executable. Computers are fun!
Additionally:
std.os
for WASI targets (#11021)BlockIoProtocol
std.os.panic
implementation for NVPTX.There were minimal changes to the Language Reference in 0.10.0, however, the big news is that Autodoc has been completely rewritten!
c_void
with anyopaque
.@truncate
and @intCast
for clarity.This feature is still experimental.
Zig gained a first (experimental) implementation of automated doc generation in version 0.6.0. That version integrated with the Bootstrap Compiler and, more specifically, it observed the data resulting from semantic analysis.
With the new Self-Hosted Compiler we needed to re-implement the main part of the system (now internally named Autodoc) to make it consume the new data format, but we took the opportunity of also re-designing its entire approach to data collection.
The first Autodoc proof-of-concept (from Zig 0.6.0) relied entirely on semantic analysis data, which had the main downside of only showing the information that had been typechecked. The most annoying result of this fact was that the docs would leave holes in the documentation, making them feel unreliable.
The new Autodoc is instead based on a two pass approach.
The main source of information consumed by Autodoc is ZIR, an untyped intermediate representation close to an AST, which gets generated unconditionally for the entire codebase everytime a Zig project is built. This makes sure that Autodoc doesn't have blind spots.
The problem with ZIR is that it is untyped, which means that it describes the program before any comptime logic is run. This means that ZIR alone won't be enough to find out all the concrete instantiations of a generic type, for example.
To solve this problem, we plan to use ZIR information as the main skeleton for Autodoc, and to overlay semantic analysis information on top of that, in order to obtain both full coverage and also full resolution of comptime metaprogramming.
Autodoc is still a work in progress and in fact it is still relying on ZIR information only. There is a tremendous amount of work ahead of us and, while we encourage you to check out Zig's stdlib docs, there are plenty of basic things that still need to be implemented. If you see something missing or broken in Autodoc, please file an issue or, even better, consider becoming a contributor.
In Zig 0.9.x and the Bootstrap Compiler, the type fn () void
is a function pointer and acts like a pointer. However, the language spec, which is not written yet, will have this type be a function body type. It must be compile-time-known, and in order to get a function pointer, *const fn () void
must be used.
The Self-Hosted Compiler implements this correctly, which means your project will likely break everywhere that you use a function pointer.
Here is the problem most people will run into:
The solution becomes obvious upon reading the compile error notes:
- fnPtr: fn () void,+ fnPtr: *const fn () void,
In previous releases of Zig there have been some nasty bugs that unfortunately did not get fixed for a long time, resulting in packed structs having a deservedly poor reputation. Finally, the situation is resolved. Packed structs have been reworked, both in terms of the Zig language specification, as well as the implementation.
Now, one can reason about packed structs in a similar way as one reasons about enums - that is - they are merely integers wearing fancy hats.
Each packed struct has a backing integer which defines the ABI of the type as well as the memory layout. The backing integer can be specified explicitly, or it can be inferred from the total bits of all the fields. If the backing integer is explicitly specified, then a compile error enforces that all the fields add up to that number of bits:
The fields are specified in order starting from logically least-significant and ending with logically most-significant within the backing integer. The backing integer is stored in memory with the same endianness as the equivalent integer type for that target. This makes Zig's packed structs have a well-defined memory layout, unlike in C where the memory layout is implementation-defined and therefore usually cannot be relied upon.
In the near future, the syntax will change to avoid confusion with C's packed structs,
which is a different concept that relates to padding. In Zig, such a struct is done by
using extern
and setting each field's alignment to 1 byte:
The inline
keyword can now be placed in front of
switch
cases. This effectively turns a runtime-known value into a
compile-time-known value:
In this example, we take a number that was passed in as a command line parameter at runtime,
and pass it to a function that requires a comptime
parameter. This
works because the switch
gets inlined - 100 prongs are
generated, similar to unrolling an inline for
loop that looped from
1 to 100.
Let's look at a less contrived example. Here is the use case where this pattern is the most useful: dynamic dispatch for enums and tagged unions.
Before:
pub fn iterate(base: *Node, index: usize) ?*Node {
inline for (@typeInfo(Tag).Enum.fields) |field| {
const tag = @intToEnum(Tag, field.value);
if (base.tag == tag) {
return @fieldParentPtr(tag.Type(), "base", base).iterate(index);
}
}
unreachable;
}
After:
pub fn iterate(base: *Node, index: usize) ?*Node {
return switch (base.tag) {
inline else => |tag| @fieldParentPtr(tag.Type(), "base", base).iterate(index),
};
}
Unfortunately, we cannot use this feature in the Standard Library or the Self-Hosted Compiler yet, because this feature is not implemented in the Bootstrap Compiler. The next steps to accomplish this will be enhancing the C Backend until we can replace the bootstrap compiler.
See the full inline switch documentation in the Language Reference.
Similar to how the ==
operator can be used to check if
an optional is null
, it can now also be used to check if an
error union is a specific error code:
Before, a pointer to a zero-bit type was itself a zero bit type. Now, pointers to zero-bit types store addresses the same as regular pointers.
This change is unlikely to affect many codebases. A use case where it might be
relevant is representing a struct that has a variable-sized last field,
as demonstrated by the name
field here:
This assertion does not pass with the Bootstrap Compiler but it does with the Self-Hosted Compiler:
However, pointers to comptime-only types are still zero bit types, such as
*comptime_int
.
See the Language Reference: @min @max
zig fmt will automatically perform the rename.
The following builtins no longer have types as the first parameter:
Instead, the type is inferred from the operand. This makes the builtins more ergonomic for SIMD.
The ++
and **
operators now support
runtime-known operands, as long as the length is compile-time-known:
This may be a breaking change for some people who were relying on Zig to automatically
evaluate the operands in a comptime
scope, like this:
The previous behavior
can be obtained with explicit use of the comptime
keyword:
- const a = foo() ++ "bb";+ const a = comptime foo() ++ "bb";
This language change is implemented in Self-Hosted Compiler only.
Zig 0.9.x and the Bootstrap Compiler allow this code:
Zig 0.10.0 does not allow this.
Address of temporaries produces constant pointers for safety reasons.
To fix this code, extract the temporary into an explicit var
.
This makes the mutable temporary more obvious, which is safer.
- const arena = std.heap.ArenaAllocator.init(std.heap.page_allocator).allocator();+ var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ const arena = arena_state.allocator();
Now that there are multiple competing Code Generation backends, it may
be necessary to work around bugs in one of them.
Similarly, as Zig gains
popularity,
there may be third party compiler implementations
which have quirks. Zig now provides a comptime
enum
value so that one can use conditional compilation to work around such issues.
Documentation comments are reproduced here:
Please read the documentation comments carefully before using this value. In summary, you can work around compiler issues by doing something like this:
const FnPtr = switch (builtin.zig_backend) {
.stage1 => fn()void,
else => *const fn()void,
};
Zig now has f80
, a primitive type that provides
x86 extended precision format.
This type is fully supported on all systems. On those which lack hardware
support for this type, softfloat implementations are provided by compiler-rt.
When would you use this? Well if you were
implementing a C compiler for instance.
It is also required for C ABI Compatibility - for some C targets
long double
is an alias for f80
.
This is implemented in both the Bootstrap Compiler and Self-Hosted Compiler.
Zig 0.9.x allows inspecting certain kinds of extra information about declarations via @typeInfo, but 0.10.0 does not (#10753):
The data field looked like this:
pub const Data = union(enum) {
Type: type,
Var: type,
Fn: FnDecl,
};
This was removed because it was an unnecessary complication. There are already alternatives for each of these fields:
@TypeOf(@field(a, b))
@field(a, b)
(after checking the @TypeOf
)@field(a, b)
(after checking the @TypeOf
)This is implemented in both the Bootstrap Compiler and Self-Hosted Compiler.
Zig 0.9.x allows declaring a struct field anytype
which made the struct be a compile-time-only type and made the field's type
allowed to be changed during comptime
code execution.
This was removed in order to simplify the language (#10766).
This is implemented in both the Bootstrap Compiler and Self-Hosted Compiler.
New builtin: @addrSpaceCast
Have a look at this code:
const directions = [_]isize{
-pitch-1,
-pitch,
-pitch+1
-1,
1,
pitch-1,
pitch,
pitch+1,
};
See the bug? It took me a minute.
Answer: the ,
is missing from the third item in the list.
This makes the list 7 items long instead of 8, and the third item
-pitch+1-1
.
This code is now a compile error due to a new rule in the language: misleading whitespace is a compile error. In this case the misleading whitespace is being inconsistent on either side of a binary operator.
More compile errors for misleading whitespace are planned.
It is no longer possible to discard a local variable or parameter that is referenced:
The fix is simple - delete the pointless discard. This compile error dovetails with the compile error for unused locals introduced in Zig 0.9.0. Together, these compile errors ensure that when you are reading Zig code, whether it is yours or some random codebase on the Internet, you can be assured that all locals are used somewhere, and that any discarded locals are otherwise unused.
This is a divisive feature of Zig. Generally, those with large codebases favor the errors since they help prevent bugs and aid refactoring, while those with small codebases find these errors annoying and unproductive.
There is an upcoming autofix feature that unfortunately did not make it in time for 0.10.0 which can be used by those who find this pair of compile errors problematic for their workflow.
This new compile error helps catch bugs (#11963) (#12417):
This caught two C header translation mistakes in haiku.zig
.
The Win64 calling convention is the same as Cdecl on Windows
x64, however using the C calling convention as a stand-in for the Win64
calling convention only works when the compiler targets Windows. This
means, before Zig 0.10.0, one was not able to create a function with the
Win64 calling convention when targeting an OS other than Windows.
Now the Win64 calling convention can be used from Zig code on any target,
using std.builtin.CallingConvention.Win64
(#11585).
Also added in this release:
PtxKernel
AmdgpuKernel
This is implemented in both the Bootstrap Compiler and Self-Hosted Compiler.
The sub-project of parsing inline assembly in the Self-Hosted Compiler has been delayed in the past due to the large tables of instructions that would be declared in source files, likely running into the unwieldly memory consumption properties of the Bootstrap Compiler. But now that we are shipping a stage3 build, this obstacle has been overcome.
Zig 0.9.x allowed comptime
expressions to produce the
assembly template for inline assembly. For most of the 0.10.0 release cycle,
Zig emitted a compile error when inline assembly source code was not a string literal,
however, that restriction was lifted just before this 0.10.0 release.
Zig is at a crossroads right now with regards to inline assembly. On one hand, there is this proposal: parse inline assembly syntax according to a set of dialects; integrate inline assembly more closely with the zig language
Meanwhile, other stakeholders have rightfully pointed out that inline assembly abstractions based on string concatenation are easy to reason about and do not require any more language features added.
Either way, Zig needs to gain the ability to parse inline assembly for all targets, so that it can get lowered to the LLVM backend as well as the other Code Generation backends, so that error handling, semantic analysis, and language features of inline assembly can be uniform across backends, something that would not be the case if LLVM is sometimes responsible for assembly code parsing and sometimes not.
Whichever path Zig ends up going down, we will make sure to involve the users who will ultimately be affected, such as the Zig Embedded Group, and the various folks who have started operating system projects written in Zig.
foo() and false
as well as bar() or true
are now compile-time-known expressions. The left-hand side is allowed to contain
runtime side effects, which are preserved as expected. Allows code like this to compile:
Note that side effects are still respected, but the result can be
compile-time-known despite them. This is the same way Zig handles, e.g.
x * 0
and 0 * x
(#6768).
In the previous release, the main Zig compiler everybody used
was the bootstrap compiler, written in C++, also known as stage1.
This release changes the default to become the Self-Hosted Compiler, however,
the legacy compiler can be selected with -fstage1
.
Despite the main focus being the Self-Hosted Compiler, this release brings some improvements to the legacy compiler codebase as well:
@errorName
null termination.packed struct
(#10499)packed struct
(#11279)comptime_int
(#10393)f128
@rem
u0
CMAKE_PREFIX_PATH
env var (#13242)The main feature of this release cycle is the début of the self-hosted compiler.
It is now enabled by default, however the Bootstrap Compiler is available
behind the -fstage1
flag for those not ready to upgrade.
This compiler implementation beats the older one in terms of performance and memory usage. Here are two data points for the compiler building itself (measured on an Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz):
The new compiler implementation is slightly faster despite that, unlike the Bootstrap Compiler, it is capable of incremental compilation due to being tightly coupled with the linker. The bigger compilation speed wins will come with the Code Generation backends that do not use LLVM.
The vast majority of the development effort of this release cycle was spent on this new compiler implementation, and therefore the Language Changes are minimal. Instead, this new codebase lays the groundwork for major enhancements, mainly concerned with speeding up the edit/test/debug cycle. Now that the self-hosted compiler is no longer sucking up so much effort, you should expect to see significant, visible progress on these Roadmap milestones.
Miscellaneous improvements:
--entry [name]
to set entrypoint symbol name (#10475)--strip
is renamed to -fstrip
and -fno-strip
is introduced. Stripping is enabled by default for
ReleaseSmall
and the flag is properly forwarded to LLD.-lgcc_s
when it is redundant with compiler-rt.
-lgcc_s
ends up on the
compiler command line even though it does not belong there. In Zig we
know what -lgcc_s
does. It is an alternative to compiler-rt. With this
commit we emit a warning telling that it is unnecessary to put such
thing on the command line, and happily ignore it, since we will fulfill
the dependency with compiler-rt.
-fbuild-id
, -fno-build-id
(#3047)-z noexecstack
option (#11826)ZIG_GLOBAL_CACHE_DIR
environment variable.zig env
now includes a field for the native target.--name
operand containing folder separator.zig init-exe
/usr/bin/env
happens
to be a shell script. It will follow the shebang line until it finds an ELF file from
which to learn the native dynamic linker path. Additionally, glibc version detection is
improved.One option you have is to simply wait for 0.10.1, or even 0.11.0, before attempting an upgrade. This gives you the smoothest experience, letting other, more brave souls, help with quality assurance before you get your hands dirty. There is no shame in this; do what works for you.
If anything goes wrong in the upgrade, you can always start using -fstage1
to
get the old compiler, or put the equivalent in your build.zig file, so that
your users can continue using zig build
as usual.
exe.use_stage1 = true;
The new compiler, sometimes called "stage2" or "stage3", is in many ways better than the old compiler (also called "stage1"), however it is not yet strictly better.
All 0.10.x releases will have the -fstage1
option; the upgrade only will
become mandatory starting with 0.11.0. For some users, sticking with stage1 for
the duration of the 0.10.x release will be the best move; for others, upgrading
to self-hosted earlier will be right for them. This guide should help you
decide which category you fall into.
Although we fully intend to make the self-hosted compiler a strict improvement, that is not the case yet.
Assuming that you have decided the time is nigh, here are some tips.
Although most of the language is the same between stage1 and self-hosted, there are some incompatibilities. Here are two strategies to deal with this:
In addition to the sub-sections below, see also these Language Changes which should be considered part of the upgrade guide:
{}
vs .{}
This one is pretty simple:
.{}
is a struct literal; {}
is a void value. It has always been a bug for the Bootstrap Compiler to accept this code. Just change .{}
to {}
.
- var x: void = .{};+ var x: void = {};
This test passes with the Bootstrap Compiler despite having a critical bug: a temporary is created by taking the address of the the pass-by-value parameter which is then returned from the function.
The legacy compiler is naive, always passing structs by pointer in code such as this. Meanwhile, the self-hosted compiler efficiently takes advantage of smaller arguments such as this, passing them truly by value, revealing the bug.
Hopefully in the future Zig will have runtime safety for this, however, currently this will manifest as a use-after-free. So if you find a pointer to bogus data, double check that the pointer was not created this way.
Once the problem has been identified, the fix is simple:
- fn foo(s: S) *const i32 {+ fn foo(s: *const S) *const i32 {
C ABI compatibility is notoriously difficult when using LLVM. In Zig 0.9.x, there were several major miscompilations for functions with C calling convention, especially when using an architecture other than x86_64.
Thanks to a nicer abstraction for dealing with the C ABI, plus some good old-fashioned hard work, in 0.10.0, Zig has near full C ABI compliance on several targets:
You can have a look at the new test harness to see what exactly is covered. Each of these targets are now tested with every CI run with QEMU.
0X
with a capital X
- eg 0X0p00x
- eg 0x.0p0@ptrToInt
(#12194)\t
in object-like macro string literals (#12549)Zig now places the zig-cache
directory next to
build.zig
. If no build.zig
file is found, it
falls back to using the global cache directory (#11672).
Internally, there are now two cache modes:
incremental and whole. Whole cache mode is what
everyone is used to from previous Zig releases. It relies on hashing the full set of
inputs, including every source file that is directly or indirectly imported.
Incremental cache mode is new and is based on re-opening
existing build artifacts and rebuilding only the modified symbols. Although
Zig's incremental compilation system is still experimental, it can be
enabled with the --watch
flag.
Incremental cache mode is the default for the
self-hosted code generation backends, and for the
LLVM backend when not using the Build System. Because the compiler
does not yet serialize and restore its state, without the --watch
flag,
this will result in recompiling with every invocation of the compiler. On the other hand,
it results in less garbage accumulating into the zig-cache
directory.
The following code, when run with Zig 0.9.1, will invoke undefined behavior:
As you can see, with Zig 0.10.0, this problem is caught with a safety check. A future enhancement will further refine this to make the stack trace point at the switch expression rather than the prong.
You can find referenced-by traces in a few other example snippets throughout this document, but here is illustrated a real world example that was particularly helpful:
Here I tried to build the Self-Hosted Compiler for WASI but ran
into some code that incorrectly tried to reference std.os.PATH_MAX
even though no such definition exists for the WASI target.
You can see that without the referenced-by trace, this bug would have been annoying to fix, but with it, the fix was trivial.
Zig's error return traces are a resource-efficient alternative to the tracebacks provided by exceptions in other languages. With the default limit of 32 stack frames, only 256 bytes per-thread are needed to maintain the error trace.
A small quality-of-life improvement Vexu
thought of while trying to
figure out where an error.GenericPoison
was coming from (#12889).
Example situation:
Before, it did this instead:
This also makes @panic
show error return trace in
these same situations which you can test by uncommenting lines in the
example.
Prior to this release, error return traces suffered from a major
drawback: handled errors were never cleaned up and would pollute the error
trace. Zig now intelligently removes any error traces corresponding to
errors handled in a catch { ... }
or
if ... else |err| { ... }
block.
pub fn main() !void {
makeEggs() catch {
if (money < 10)
return error.OutOfMoney; // If executed: trace shows makeEggs()'s error, followed by this line
try buyIngredients(); // On error: trace shows makeEggs()'s error, then buyIngredients()'s error
};
// makeEggs() removed from the trace here
try serveBreakfast(); // On error: trace shows serveBreakfast()'s error only
}
Very long error return traces are also trimmed correctly and print a helpful note to let you know about the missing entries:
...
./test_err.zig:5:5: 0x10224b39e in recursive__anon_1055 (test_err)
return recursive(N - 1);
^
./test_err.zig:5:5: 0x10224b35e in recursive__anon_1054 (test_err)
return recursive(N - 1);
^
(98 additional stack frames skipped...)
There is one restriction to be aware of: error traces do not follow
errors into local variables (var
) by default.
This restriction is what allows all functions in the thread to share the same 256-byte
error trace storage.
The new std.debug.Trace API allows users to copy and store error return traces for mutable variables where needed.
Here is a real world example. Before, one might get debug output that looks like this:
After this improvement, we see this instead:
This was an old issue that plagued Zig developers for a long time. We have Cody Tapscott to thank for solving it.
While the default backend is still LLVM, Zig is starting to invest in providing its own code generation. The primary motivation for this is compilation speed. However, there is also a benefit to providing a bootstrapping process that does not depend on C++, CMake, Python, libxml, zlib, and zstd, all of which LLVM require in order to build. Furthermore, there is value in providing healthy competition so that compiler backends do not become a monoculture.
As a point of comparison, a stripped release build of Zig with LLVM is 169 MiB, while without LLVM (but with all the code generation backends you see here) it is 4.4 MiB.
One of the visible improvements to the self-hosted Wasm backend is its capabilities to generate code. The backend is now capable of passing 75% of the behavior tests, compared to 13% in Zig 0.9.x. At this point, most Zig features are supported. For features of which the WebAsssembly architecture has no support, the backend is already capable of generating calls into compiler-rt, and correctly link with it. This is thanks to the C ABI Compatibility work that was done during this release. This also benefitted the LLVM backend with regards to its WebAssembly target, as the logic for the C ABI implemtation is fully shared between the backends. This will ensure that regardless of the chosen backend, the function signatures they generate are equal. One notable feature that was previously missing was support for DWARF Linking when targetting WebAssembly. This is now fully supported in the LLVM backend, and partly in the native backend. This allows for a great developer experience when targetting WebAssembly. The goal is to add full support to the native backend by its next release.
Some invisible improvements were done also. The backend was refactored to be aware of the Wasm stack and leave locals there when possible, avoiding unnecessary stores. This means we not only generate fewer instructions, but also create fewer locals. Besides this, the backend now integrates with Liveness Analysis, reusing locals that are no longer being referenced. All of those changes are with the goal of making the native backends not only compile fast, but also generate code which runs faster than when using the LLVM backend.
Some notable changes that were partly responsible for the above include:
lowerUnnamedConst
(#10983)A new contributor jacobly0 came out of nowhere and did a massive amount of work to Zig's C backend, reaching these notable milestones:
x86_64-linux
(with and without libc).Additionally, in this release zig.h moved to become an installation file. Instead of zig.h being baked into the compiler binary, it is a header file distributed along with all the other header files distributed with Zig (#11643).
Soon, this backend will be used to replace the Bootstrap Compiler with a C implementation that outputs C code (#5246).
Although this backend remains experimental, it has seen considerable progress during this release cycle. Notable milestones reached:
x86_64
encoder to handle most of general purpose register
encodings such as I
, OI
, MI
, RM
,
MR
, and RMI
.MV
, RVM
and RVMI
.All of the above culminates in the x86_64 self-hosted backend passing 875/1365 (64%) of the behavior tests compared to the LLVM backend. It will need to get very close to 100% before being enabled by default for debug mode.
This backend remains experimental, however it now supports error unions, structs, and switch statements and is thereby passing 790/1365 (58%) of the behavior tests. It will need to get very close to 100% before being enabled by default for debug mode.
This backend remains experimental, however it now supports error unions, structs, and switch statements and is thereby passing 560/1365 (41%) of the behavior tests. It will need to get very close to 100% before being enabled by default for debug mode.
The self-hosted linker is tightly coupled with the Self-Hosted Compiler.
There has been progress on ELF and COFF however they are not generally usable yet.
By default, LLD will be used for these object formats, however, one can opt in to
the self-hosted linker with -fno-LLD
.
The incremental MachO linker, like its traditional linking counterpart, has seen a major rewrite where Zig now lays out the incrementally linked binary such that there is one section per segment. This might sound wasteful but it allows Zig to use the quick file space allocation algorithm used already in the ELF and COFF linkers.
How does it work? By enclosing a single section within a single segment Zig is able to disassociate the file offsets from the allocated virtual memory addresses. This provides the uncanny ability to reorder and move the sections within the underlying binary file without affecting the ordering in the virtual memory space. This implies that if the section grows beyond what was originally allocated, Zig only needs to copy its contents in the file to a new location without any virtual address space recalculations unless the section has also exceeded its allocated virtual address space. But even then only some atoms are affected in subsequent sections in the virtual address space.
Thanks to this trick, Zig is on the path of achieving truly incremental linking for MachO which should also make implementing hot-code swapping for MachO much simpler and more efficient.
For a more in-depth look into the changes involved have a look at this pull request: macho: rewrite incremental linker, and init splitting of linking contexts
In addition, the following improvements have been made:
b/bl
's 28bits signed displacement which roughly equates to 128MB of compiled
machine code section. The linker now extends the jump range via thunks which
load the target address into the scratch register followed by a branch via
register blr
(#13260) (#13274)CodeSignature
to accept entitlementsCS_LINKER_SIGNED
flag in code signature generated by Zig__PAGEZERO
segment. Add misc improvements to the pipeline, and correctly
transfer the requested __PAGEZERO
size to the linker.-pagezero_size
is always in hex. This matches the behavior of other linkers out
there including ld64
and lld
.-search_dylibs_first
linker option__DATA
and __LINKEDIT
with 0s if required. If there are zerofill
sections, the loader may copy the contents of the physical space in file directly into memory
and attach that to the zerofill section. This is a performance optimisation in the loader
but requires us, the linker, to properly zero-out any space between __DATA
and
__LINKEDIT
segments in file. This is of course completely skipped if there are no zerofill sections present.-headerpad_size
and -headerpad_max_install_names
options-dead_strip_dylibs
linker flag-needed-*
and -needed_*
family of flags-weak-lx
and -weak_framework x
flags-dead_strip
flag. Dead-code stripping is on by default for release build modes, and
off for debug builds, but can be opted in/out via --gc-sections
and --no-gc-sections
flags.__mh_execute_header
as referenced dynamically-force_load=archive_path
flagThe COFF linker has been reimplemented from scratch giving our Windows users
a chance to finally be able to play with the incremental linking
(using the --watch
flag and x86 Backend via -fno-LLVM
).
In addition, the linker now matches the implementation progress of
both ELF and MachO, and hence, it is now possible to run the behavior tests on
x86_64-windows
successfully:
For more detailed description of the implementation, have a look at this pull request: coff: implement enough of the incremental linker to pass behavior and incremental tests on Windows
The self-hosted ELF linker remains experimental, capable of incremental compilation of simple Zig programs but not capable of acting as a traditional linker.
Major progress has been made on the in-house WebAssembly linker. Zig is nearing feature-parity with wasm-ld, which is the linker the compiler uses today when targeting WebAssembly. The linker is now capable of linking with most object files, while also being able to link with archive files such as compiler-rt. During linking, it now correctly relocates Debugging information. The next milestone would be to make the in-house linker the default linker. To accomplish this, the following features must be implemented:
Those features will bring the linker to near feature parity. By making the switch before being fully feature complete, we can begin the debugging process sooner, as well as focusing on optimizing the memory usage and runtime performance of the linker.
The following changes highlight some of the features mentioned above:
The incremental DWARF emitter/linker is now capable of emitting correctly
relocated debug info for x86_64
, arm
and
aarch64
architectures on Linux and macOS. The state of
implementation varies slightly between architectures with x86_64
being the furthest ahead.
Example progress includes correctly generating DWARF debug info for
slices including []const u8
for passing as formal parameters.
Breaking on a function accepting a slice in gdb
will now yield
the same behavior as the Bootstrap Compiler and LLVM backend:
Both a
and b
can now be inspected in the debugger:
Breakpoint 1, sumArrayLens (a=..., b=...) at arr.zig:59
(gdb) p a
$1 = {ptr = 0x7fffffff685c, len = 5}
(gdb) p b
$2 = {ptr = 0x7fffffff683d "\252\252\252\\h\377\377\377\177", len = 3}
(gdb)
This release saw a general rewrite of the majority of the linker in the spirit of data-oriented design which led to:
lld
and ld64
In the benchmarks below, zld
refers to our linker as a standalone binary
(which you can find at kubkon/zld),
lld
to LLVM's linker, and ld64
to Apple's linker.
I should also point out that we are still missing a number of optimisations in
the linker such as cstring deduplication, compression of dynamic linker's
relocations, and synthesising of the unwind info section, so this difference
between zld
and other linkers will most likely shrink a little.
I've run the benchmarks on a MacBook Pro 16" with M1 Pro SoC and 32GB RAM. The generated code for the linker was not using hardware sha256 extensions.
Linking redis-server binary:
Linking Self-Hosted Compiler binary:
The incremental compilation that utilises Zig's
self-hosted native backends has
seen the addition of a vastly improved and easily extensible test harness. In
order to add a new incremental test (emulating the --watch
flag), it is now
only required to drop a Zig source file in the test harness directory with a
specially crafted comment instructing the harness of several test parameters
such as test target, and that's it. Thanks to this dynamic approach there is no
longer any need in recompiling the harness (#11530) (#11572).
Linkers also have received a new test harness, which is based on the
StandaloneTestContext
and CheckFileStep
, and as
such assumes each test case specifies a build.zig
script where
we instruct the harness what the link flags/conditions are the expected
results such as a runtime result or a greppable contents of the generated
binary file (#11910).
Notable features:
CheckObjectStep
specialises CheckFileStep
into directed (surgical) MachO file fuzzy searches. This will be the
building block for comprehensive linker tests.The Zig standard library is still unstable and mainly serves as a testbed for the language. After the Self-Hosted Compiler is completed, the language stabilized, and Package Manager completed, then it will be time to start working on stabilizing the standard library. Until then, experimentation and breakage without warning is allowed.
comptime
struct fields in
mem.zeroes()
(#10406)mem.zeroes
to work at comptime with extern union (#10797)first
and reset
method to SplitIterator
and SplitBackwardsIterator
ascii.indexOfIgnoreCase
with Boyer-Moore-Horspool algorithm.error.TimerUnsupported
.
In this case, only explicit calls to refresh() will cause the progress
line to be printed.unicode.replacement_character
dirent.h
to std.c (#11598)powi
: use standard definition of underflow/overflow, implement u0
, i0
, i1
edge case (#11499)cast
return optional instead of an error.comptime_int
degreesToRadians
and radiansToDegrees
XDG_DATA_HOME
OpenMode
enum instead of read/write flags.fs.Dir.Iterator
twice (#10404)Dir
into IterableDir
(#12060)ENOENT
(#12211)WalkerEntry.dir
not always being the containing dirRyan Liptak improved the performance of std.fs.Dir.deleteTree
(#13073).
The new implementation has the same constraints:
The new implementation has better performance than rm -rf
(at least
on Linux - see benchmark details):
$ hyperfine "./rm-0.10.0 my-app-copy" "./rm-0.9.1 my-app-copy" "rm -rf my-app-copy" --prepare="cp -r my-app my-app-copy" --warmup 1 --runs 20
Benchmark #1: ./rm-0.10.0 my-app-copy
Time (mean ± σ): 382.7 ms ± 7.8 ms [User: 13.6 ms, System: 358.9 ms]
Range (min … max): 372.2 ms … 395.9 ms 20 runs
Benchmark #2: ./rm-0.9.1 my-app-copy
Time (mean ± σ): 701.2 ms ± 28.3 ms [User: 36.8 ms, System: 646.2 ms]
Range (min … max): 673.4 ms … 799.4 ms 20 runs
Benchmark #3: rm -rf my-app-copy
Time (mean ± σ): 423.9 ms ± 35.4 ms [User: 17.5 ms, System: 379.4 ms]
Range (min … max): 391.6 ms … 518.9 ms 20 runs
Summary
'./rm-0.10.0 my-app-copy' ran
1.11 ± 0.10 times faster than 'rm -rf my-app-copy'
1.83 ± 0.08 times faster than './rm-0.9.1 my-app-copy'
The new implementation accomplishes this by having a fixed-size buffer on the stack, temporarily falling back to a slower algorithm when it encounters deeply nested file system trees. The fallback implementation is also optimized to reduce the number of syscalls emitted.
addOne
, pop
, and popOrNull
(#11553)We have Frank Denis to thank for Zig's state-of-the-art cryptography capabilities. In this release:
edwards25519.clearCofactor()
.
It is useful to check for low-order points in protocols where it
matters and where clamping cannot work, such as PAKEs.Zig 0.10.0 adds support for ECDSA signatures (#11855).
ECDSA is the most commonly used signature scheme today, mainly for historical and conformance reasons. It is a necessary evil for many standard protocols such as TLS and JWT.
It is tricky to implement securely and has been the root cause of multiple security disasters, from the Playstation 3 hack to multiple critical issues in OpenSSL and Java.
This implementation combines lessons learned from the past with recent recommendations.
In Zig, the NIST curves that ECDSA is almost always instantied with use formally verified field arithmetic, giving us peace of mind even on edge cases. And the API rejects neutral elements where it matters, and unconditionally checks for non-canonical encoding for scalars and group elements. This automatically eliminates common vulnerabilities.
ECDSA's security heavily relies on the security of the random number generator, which is a concern in some environments.
This implementation mitigates this by computing deterministic nonces using the conservative scheme from Pornin et al. with the optional addition of randomness as proposed in Ericsson's "Deterministic ECDSA and EdDSA Signatures with Additional Randomness" document. This approach mitigates both the implications of a weak RNG and the practical implications of fault attacks.
Project Wycheproof is a Google project to test crypto libraries against known attacks by triggering edge cases. It discovered vulnerabilities in virtually all major ECDSA implementations.
The entire set of ECDSA-P256-SHA256 test vectors from Project Wycheproof is included here. Zero defects were found in this implementation.
Instead of raw byte strings for keys and signatures, we introduce the
Signature
, PublicKey
and SecretKey
structures.
The reason is that a raw byte representation would not be optimal. There are multiple standard representations for keys and signatures, and decoding/encoding them may not be cheap (field elements have to be converted from/to the montgomery domain).
The std.crypto.sign.ed25519
API has been changed to adopt the
same structure as the ECDSA API. This is a breaking change.
Instead of raw bytes, Ed25519 keys are now represented with the newly
introduced Ed25519.PublicKey
and Ed25519.SecretKey
types.
Similarly, the Ed25519.Signature
type is now used to store
signatures.
In Zig 0.9.x, key pair creation, signature computation and verification were typically done this way:
const Ed25519 = std.crypto.sign.Ed25519;
// Key pair creation
const key_pair = try Ed25519.KeyPair.create(null);
// Signature computation
const sig = try Ed25519.sign("message", key_pair, null);
// Signature verification
try Ed25519.verify(sig, "message", key_pair.public_key);
In Zig 0.10.0, the code above becomes:
const Ed25519 = std.crypto.sign.Ed25519;
// Key pair creation
const key_pair = try Ed25519.KeyPair.create(null);
// Signature computation
const sig = try key_pair.sign("message", null);
// Signature verification
try sig.verify("message", key_pair.public_key);
In Zig 0.9.x, the entire message to be signed had to be loaded in memory before signing. In Zig 0.10, the message can be streamed, which is especially useful for large messages.
For example, the following code signs a message that is too large to fit in memory:
var signer = try key_pair.signer(null);
signer.update("message_part_1");
signer.update("message_part_2");
signer.update("message_part_3");
const sig = signer.finalize();
Similarly, the following code verifies a multi-part message:
var verifier = try sig.verifier(key_pair.public_key);
verifier.update("message_part_1");
verifier.update("message_part_2");
verifier.update("message_part_3");
try verifier.verify();
Zig 0.10.0 adds the Xoodoo permutation, preparing for Gimli deprecation (#11866).
Gimli was a game changer. A permutation that is large enough to be used in sponge-like constructions, yet small enough to be compact to implement and fast on a wide range of platforms.
And Gimli being part of the Zig standard library was awesome.
But since then, Gimli entered the NIST Lightweight Cryptography Competition, competing againt other candidates sharing a similar set of properties.
Unfortunately, Gimli didn't pass the 3rd round.
There are no practical attacks against Gimli when used correctly, but NIST's decision means that Gimli is unlikely to ever get any traction.
So, maybe the time has come to move Gimli from the standard library to another repository.
We shouldn't do it without providing an alternative, though. And the best candidate for this is probably Xoodoo.
Xoodoo is the core function of Xoodyak, one of the finalists of the NIST LWC competition, and the most direct competitor to Gimli. It is also a 384-bit permutation, so it can easily be used everywhere Gimli was used with no parameter changes.
It is the building block of Xoodyak (for actual encryption and hashing) as well as Charm, that some Zig applications are already using.
Like Gimli that it was heavily inspired from, it is compact and suitable for constrained environments.
This change adds the Xoodoo permutation to std.crypto.core.
The set of public functions includes everything required to later implement existing Xoodoo-based constructions.
In order to prepare for the Gimli deprecation, the default CSPRNG was changed to a Xoodoo-based that works exactly the same way.
On x86_64 and AArch64, SHA-256 now takes advantage of specialized CPU features when available.
This can affect MachO linking performance due to Apple's signature requirement.
On my Intel(R) Core(TM) i9-9980HK, for example, this CPU feature is not available, and I therefore do not observe any improved performance. However, Apple's M1 laptops do have this feature, and I observed SHA-256 increase from 199 MiB/s to 2.1 GiB/s in the crypto benchmark.
sched_yield
to yield
and move it to std.Thread
panicImpl
sleep is unreachable (#11569)Method
and Status
(#10661)HOSTUNREACH
for blocking and non-blocking connectsnet.getAddressList
: fix segfault on bad hostname (#12065)AFNOSUPPORT
error to bind (#12560)tcdrain
on Linuxstd.os
now handles error.UnreachableAddress
in send()
setsockopt
returning ENODEV
Networking has mostly been quiet until now, but it is about to get lit as we dive into the Package Manager in the next release cycle.
std.testing.zig_exe_path
in favor of ZIG_EXE
environment variable (#11633)zig test
(#10859)refAllDeclsRecursive
functionThere is a new API that checks for memory leaks (and other
problems) caused by allocation failures by taking advantage of the
FailingAllocator
and inducing failure at every
allocation point within the provided function (#10586).
Below is the function's giant doc comment in a more readable form:
Exhaustively check that allocation failures within test_fn
are handled
without introducing memory leaks. If used with the testing.allocator
as the
backing_allocator
, it will also perform memory sanitization when
runtime safety is enabled.
The provided test_fn
must have a std.mem.Allocator
as its first argument,
and must have a return type of !void
. Any extra arguments of test_fn
can
be provided via the extra_args
tuple.
Any relevant state shared between runs of test_fn
must be reset within test_fn
.
Expects that the test_fn
has a deterministic number of memory allocations. An error will be returned if non-deterministic allocations are detected.
The strategy employed is to:
Here is an example of using a simple test case that will cause a leak when the
allocation of bar
fails (but will pass normally):
When the test case is converted to use the new
checkAllAllocationFailures
API, the test fails:
Running this test will show that foo
is leaked when the allocation of
bar
fails. The simplest fix, in this case, would be to use
defer
:
std.debug
now has better resource management (#11485)first_address
on Windows (#6687)FailingAllocator
and use it to improve
checkAllAllocationFailures (#11919)This is a new API that you can use to efficiently save small stack traces for later, along with relevant data, so that when a problematic behavior is observed, you can dump the corresponding stack trace (#11819):
This provides insight about the state of the program at a cross-section according to specific objects, which can lead directly to identifying bugs that otherwise would be completely opaque.
By default the Trace
objects are empty in
release modes, so the cost is only paid when debugging. Even when debugging, however,
the size of the Trace objects are small - only one usize
per stack
frame. It defaults to 2 events, 4 stack frames, but can be configured with
std.debug.ConfigurableTrace
.
Hadrien Dorio provided a new deflate implementation (#10552).
Old API:
inflateStream(reader: anytype, window_slice: []u8)
New API:
decompressor(allocator: mem.Allocator, reader: anytype, dictionary: ?[]const u8)
compressor(allocator: mem.Allocator, writer: anytype, options: CompressorOptions)
The implementation is a port of the compress/flate package from Go, and appears to be about 2-3x faster.
Big thanks to Ryan Liptak for fuzz testing.
There is still a lot of room for performance improvement beyond this.
zig.isValidId
with empty string (#11104)fmt.formatAsciiChar
respect options
parameterFull list of the 452 bug reports closed during this release cycle.
Many bugs were both introduced and resolved within this release cycle.
Zig has known bugs and even some miscompilations.
Zig is immature. Even with Zig 0.10.0, working on a non-trivial project using Zig will likely require participating in the development process.
When Zig reaches 1.0.0, Tier 1 Support will gain a bug policy as an additional requirement.
A 0.10.1 release is planned. Waiting until 0.10.1 to upgrade is recommended for users who desire a more stable experience.
zig build
now respects the ZIG_LIB_DIR
environment variable.expected_exit_code
to be null
fmtId
for type names.std.build.Pkg.path
to std.build.Pkg.source
(#11557)comptime_int
in OptionsStep.addOption
-fstack-protector
runPkgConfig
pubStep.cast
for OptionsStep
and CheckObjectStep
unwind_tables
integration.This release of Zig upgrades to LLVM 15.0.3.
Note that the Zig 0.9.x series used LLVM 13 - there is no Zig release that depends on LLVM 14.
llvm-config
, especially for consistent static/shared linking (#12136)llvm-config
executables in PATH.Zig ships with the source code to musl. When the musl C ABI is selected, Zig builds static musl from source for the selected target. Zig also supports targeting dynamically linked musl which is useful for Linux distributions that use it as their system libc, such as Alpine Linux.
This release upgrades from v1.2.2 to v1.2.3.
The bad news is that although glibc 2.35 and 2.36 have already been released, Zig is behind in adding cross-compilation support for them due to exploring the Universal Headers project. The good news, however, is that Zig can now integrate with system glibc that is newer than what Zig can provide (#12797).
Additionally:
libresolv
stubs (#12629)_DYNAMIC_STACK_SIZE_SOURCE
(#10713)Zig ships with the source code to mingw-w64. When targeting *-windows-gnu and linking against libc, Zig builds mingw-w64 from source for the selected target.
This release updates the bundled mingw-w64 source code to v10.0.0.
Zig now passes -D__USE_MINGW_ANSI_STDIO=0
for crt files. This was supposed
to be happening all along, and it was a bug that Zig did not do this before (#7356). Thanks to
Martin Storsjö for
helping us discover this on IRC. This fix means that Zig no longer carries
any patches on top of upstream mingw-w64 sources!
Speaking of Martin, he has just a couple days ago signed up for GitHub Sponsors. He does brilliant work on mingw-w64, and he has patiently helped us when we go into the IRC channel asking for troubleshooting tips from Zig's unusual way of building mingw-w64 from source. Most people would (understandably) say, "sorry, that's unsupported." We are proud to be Martin's first sponsor and hope you will join us in showing support!
zig cc
is Zig's drop-in C compiler tool. Enhancements in this release:
--sysroot
arg (#10568).--whole-archive
, -whole-archive
--no-whole-archive
, -no-whole-archive
-s
, --strip-all
-S
, --strip-debug
--hash-style
linker parameter (applies to ELF).zig cc
would
incorrectly punt to clang because it thought there were no positional
arguments.a.out
if outpath is unspecified (#12858)zig cc
match the equivalent behavior of
zig build-exe
with regards to caching. That is - it will
cache individual .c to .o compilations (with the usual exceptions), but
will always repeat the linking process so that incremental linking has
a chance to happen.
zig build-exe
instead of zig cc
and using the
--enable-cache
parameter, matching what the Build System does.
-Wl,--export=<symbol>
--subsystem
linker flag (#11396)-fstack-check
, -fno-stack-check
/dev/null
-o /dev/null
equivalent to
-fno-emit-bin
because otherwise the atomic rename into
place will fail. This also makes Zig do less work, avoiding pointless
file system operations.-l :FILE
syntax (#10851)-Wl,--soname
-g
is not present.-z nocopyreloc
.-no-pie
.-O0
rather than -Og
to Clang for Debug builds (#11949)--verbose
as an alias for -v
--compress-debug-sections
(#11863)-M
(#12369)--print-gc-sections
, --print-icf-sections
, and
--print-map
-g
flag when appropriate.zig clang
internally, Zig will now
use a response file (.rsp) if the CLI length would exceed what the system supports.
This fixes a CI test failure on Windows (#12540)Please be aware that there are still open zig cc issues. However, these are use cases that never worked in the first place, making them bugs and enhancements rather than regressions.
zig c++
is equivalent to zig cc with an added -lc++
parameter, but I made a separate heading here because I realized that some people are
not aware that Zig supports compiling C++ code and providing libc++ too!
Cross-compiling too, of course:
One thing that trips people up when they use this feature is that the C++ ABI is not stable across compilers, so always remember the rule: You must use the same C++ compiler to compile all your objects and static libraries. This is an unfortunate limitation of C++ which Zig can never fix.
Improvements to libc++ in this release:
-DHAVE___CXA_THREAD_ATEXIT_IMPL
for glibc
(#13314)callconv(.Inline)
on function pointer types.--exclude
now ignores missing directories.compiler-rt is the library that provides, for example, 64-bit integer multiplication for 32-bit architectures which do not have a machine code instruction for it. In the GNU world, it is called libgcc.
Unlike most compilers, which depend on a binary build of compiler-rt being installed alongside the compiler, Zig builds compiler-rt on-the-fly, from source, as needed for the target platform. This release saw some improvements to Zig's compiler-rt implementation (#1290).
__absvsi2
, __absvdi2
, __absvti2
__negvsi2
, __negvdi2
, __negvti2
memcpy
or sqrt
.
These are typically provided by libc, but they might not be in the case of freestanding,
or depending on which third party libc is linked against.
-ffunction-sections
plus garbage collection ended up being faster.Now that the Self-Hosted Compiler is launched, we can move on with the roadmap!
The major themes of the 0.11.0 release cycle will be language changes, compilation speed, and package management.
Some upcoming milestones we will be working towards in the 0.11.0 release cycle:
Here are the steps for Zig to reach 1.0:
Having a package manager built into the Zig compiler is a long-anticipated feature. Zig 0.10.0 does not have this feature. However, all the prerequisites are finally done, and work begins now!
We will do our best to keep in mind the needs of system package maintainers while working on this language package manager.
If you want more of a sense of the direction Zig is heading, you can look at the set of accepted proposals.
Here are all the people who landed at least one contribution into this release:
Special thanks to those who sponsor Zig. Because of you, Zig is driven by the open source community, rather than the goal of making profit. In particular, these fine folks sponsor Zig for $50/month or more: