diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 934b8a4f4a..5e94a83a3a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -4,7 +4,7 @@ packages: - ninja - llvm70 sources: - - https://github.com/ziglang/zig.git + - https://github.com/ziglang/zig tasks: - build: | cd zig && mkdir build && cd build diff --git a/.gitignore b/.gitignore index 4b7bff11a6..f85fc969b1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,6 @@ # -andrewrk zig-cache/ -build/ -build-*/ -docgen_tmp/ +/build/ +/build-*/ +/docgen_tmp/ diff --git a/CMakeLists.txt b/CMakeLists.txt index a11ddfd864..9e2cb5b9d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -417,6 +417,7 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/error.cpp" "${CMAKE_SOURCE_DIR}/src/ir.cpp" "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" + "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp" "${CMAKE_SOURCE_DIR}/src/link.cpp" "${CMAKE_SOURCE_DIR}/src/main.cpp" "${CMAKE_SOURCE_DIR}/src/os.cpp" @@ -432,6 +433,10 @@ set(BLAKE_SOURCES ) set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp" "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) @@ -446,10 +451,12 @@ set(ZIG_STD_FILES "buf_set.zig" "buffer.zig" "build.zig" + "build/fmt.zig" "c/darwin.zig" "c/freebsd.zig" "c/index.zig" "c/linux.zig" + "c/netbsd.zig" "c/windows.zig" "coff.zig" "crypto/blake2.zig" @@ -485,6 +492,7 @@ set(ZIG_STD_FILES "fmt/errol/index.zig" "fmt/errol/lookup.zig" "fmt/index.zig" + "fmt/parse_float.zig" "hash/adler.zig" "hash/crc.zig" "hash/fnv.zig" @@ -589,6 +597,8 @@ set(ZIG_STD_FILES "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" + "os/netbsd/errno.zig" + "os/netbsd/index.zig" "os/path.zig" "os/time.zig" "os/uefi.zig" @@ -603,6 +613,7 @@ set(ZIG_STD_FILES "os/windows/util.zig" "os/zen.zig" "pdb.zig" + "priority_queue.zig" "rand/index.zig" "rand/ziggurat.zig" "segmented_list.zig" @@ -611,6 +622,7 @@ set(ZIG_STD_FILES "special/bootstrap_lib.zig" "special/build_runner.zig" "special/builtin.zig" + "special/compiler_rt/addXf3.zig" "special/compiler_rt/aulldiv.zig" "special/compiler_rt/aullrem.zig" "special/compiler_rt/comparetf2.zig" @@ -653,6 +665,7 @@ set(ZIG_STD_FILES "special/compiler_rt/udivmodti4.zig" "special/compiler_rt/udivti3.zig" "special/compiler_rt/umodti3.zig" + "special/fmt_runner.zig" "special/init-exe/build.zig" "special/init-exe/src/main.zig" "special/init-lib/build.zig" @@ -906,3 +919,7 @@ foreach(file ${ZIG_STD_FILES}) get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY) install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}") endforeach() + +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") diff --git a/README.md b/README.md index 2963e08887..b359775d68 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A programming language designed for robustness, optimality, and clarity. -[ziglang.org](https://ziglang.org) +[Download & Documentation](https://ziglang.org/download/) ## Feature Highlights @@ -77,36 +77,54 @@ clarity. - 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. #### Tier 4 Support * Support for these targets is entirely experimental. * LLVM may have the target as an experimental target, which means that you need to use Zig-provided binaries for the target to be available, or - build LLVM from source with special configure flags. + build LLVM from source with special configure flags. `zig targets` will + display the target if it is available. + * This target may be considered deprecated by an official party, + [such as macosx/i386](https://support.apple.com/en-us/HT208436) in which + case this target will remain forever stuck in Tier 4. + * This target may only support `--emit asm` and cannot emit object files. #### Support Table -| | freestanding | linux | macosx | windows | freebsd | UEFI | other | -|--------|--------------|--------|--------|---------|---------|--------|--------| -|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 3 | -|i386 | Tier 2 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | -|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | -|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | -|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | -|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | +| | freestanding | linux | macosx | windows | freebsd | netbsd | UEFI | other | +|-------------|--------------|--------|--------|---------|---------|------- | -------|--------| +|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | +|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|avr | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | +|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | +|xcore | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|nvptx | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|msp430 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|r600 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|arc | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|tce | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|le | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|amdil | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|hsail | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|spir | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|kalimba | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|shave | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|renderscript | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | ## Community diff --git a/build.zig b/build.zig index 5c7c5b8a18..756ec53708 100644 --- a/build.zig +++ b/build.zig @@ -49,13 +49,14 @@ pub fn build(b: *Builder) !void { .c_header_files = nextValue(&index, build_info), .dia_guids_lib = nextValue(&index, build_info), .llvm = undefined, - .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false, }; ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); var test_stage2 = b.addTest("src-self-hosted/test.zig"); test_stage2.setBuildMode(builtin.Mode.Debug); + const fmt_build_zig = b.addFmt([][]const u8{"build.zig"}); + var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); exe.setBuildMode(mode); @@ -107,6 +108,11 @@ pub fn build(b: *Builder) !void { } const modes = chosen_modes[0..chosen_mode_index]; + // run stage1 `zig fmt` on this build.zig file just to make sure it works + test_step.dependOn(&fmt_build_zig.step); + const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); + fmt_step.dependOn(&fmt_build_zig.step); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); @@ -289,8 +295,6 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 { } fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { - exe.setNoRoSegment(ctx.no_rosegment); - exe.addIncludeDir("src"); exe.addIncludeDir(ctx.cmake_binary_dir); addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); @@ -375,5 +379,4 @@ const Context = struct { c_header_files: []const u8, dia_guids_lib: []const u8, llvm: LibraryDep, - no_rosegment: bool, }; diff --git a/cmake/Findclang.cmake b/cmake/Findclang.cmake index e809ff2eca..6747e489b4 100644 --- a/cmake/Findclang.cmake +++ b/cmake/Findclang.cmake @@ -11,17 +11,28 @@ if(MSVC) find_package(CLANG REQUIRED CONFIG) set(CLANG_LIBRARIES + clangFrontendTool + clangCodeGen clangFrontend clangDriver clangSerialization clangSema + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore clangAnalysis + clangASTMatchers clangAST clangParse clangSema clangBasic clangEdit clangLex + clangARCMigrate + clangRewriteFrontend + clangRewrite + clangCrossTU + clangIndex ) else() @@ -50,17 +61,28 @@ else() endif() endmacro(FIND_AND_ADD_CLANG_LIB) + FIND_AND_ADD_CLANG_LIB(clangFrontendTool) + FIND_AND_ADD_CLANG_LIB(clangCodeGen) FIND_AND_ADD_CLANG_LIB(clangFrontend) FIND_AND_ADD_CLANG_LIB(clangDriver) FIND_AND_ADD_CLANG_LIB(clangSerialization) FIND_AND_ADD_CLANG_LIB(clangSema) + FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) + FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) + FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) FIND_AND_ADD_CLANG_LIB(clangAnalysis) + FIND_AND_ADD_CLANG_LIB(clangASTMatchers) FIND_AND_ADD_CLANG_LIB(clangAST) FIND_AND_ADD_CLANG_LIB(clangParse) FIND_AND_ADD_CLANG_LIB(clangSema) FIND_AND_ADD_CLANG_LIB(clangBasic) FIND_AND_ADD_CLANG_LIB(clangEdit) FIND_AND_ADD_CLANG_LIB(clangLex) + FIND_AND_ADD_CLANG_LIB(clangARCMigrate) + FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) + FIND_AND_ADD_CLANG_LIB(clangRewrite) + FIND_AND_ADD_CLANG_LIB(clangCrossTU) + FIND_AND_ADD_CLANG_LIB(clangIndex) endif() include(FindPackageHandleStandardArgs) diff --git a/doc/docgen.zig b/doc/docgen.zig index 45f6dc2684..cea456a98d 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -916,6 +916,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok std.zig.Token.Id.AngleBracketAngleBracketRightEqual, std.zig.Token.Id.Tilde, std.zig.Token.Id.BracketStarBracket, + std.zig.Token.Id.BracketStarCBracket, => try writeEscaped(out, src[token.start..token.end]), std.zig.Token.Id.Invalid => return parseError( @@ -1104,14 +1105,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }, } if (code.target_windows) { - try test_args.appendSlice([][]const u8{ - "--target-os", - "windows", - "--target-arch", - "x86_64", - "--target-environ", - "msvc", - }); + try test_args.appendSlice([][]const u8{ "-target", "x86_64-windows" }); } const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed"); const escaped_stderr = try escapeHtml(allocator, result.stderr); diff --git a/doc/langref.html.in b/doc/langref.html.in index 779eb6a31b..9dc0a31b23 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8,13 +8,7 @@ body{ background-color:#111; color: #bbb; - font-family: system-ui, - /* Fallbacks for browsers that don't support system-ui */ - /* https://caniuse.com/#search=system-ui */ - -apple-system, /* iOS and macOS */ - Roboto, /* Android */ - "Segoe UI", /* Windows */ - sans-serif; + font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif; } a { color: #88f; @@ -243,8 +237,9 @@ const Timestamp = struct { {#header_close#} {#header_open|Values#} {#code_begin|exe|values#} -const std = @import("std"); +// Top-level declarations are order-independent: const warn = std.debug.warn; +const std = @import("std"); const os = std.os; const assert = std.debug.assert; @@ -262,7 +257,7 @@ pub fn main() void { true and false, true or false, !true); - + // optional var optional_value: ?[]const u8 = null; assert(optional_value == null); @@ -281,7 +276,7 @@ pub fn main() void { warn("\nerror union 1\ntype: {}\nvalue: {}\n", @typeName(@typeOf(number_or_error)), number_or_error); - + number_or_error = 1234; warn("\nerror union 2\ntype: {}\nvalue: {}\n", @@ -706,15 +701,21 @@ fn divide(a: i32, b: i32) i32 { {#code_end#}

In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime, - and thus this division operation is vulnerable to both integer overflow and - division by zero. + and thus this division operation is vulnerable to both {#link|Integer Overflow#} and + {#link|Division by Zero#}.

Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.

- {#see_also|Integer Overflow|Division by Zero|Wrapping Operations#} +

+ Zig supports arbitrary bit-width integers, referenced by using + an identifier of i or u followed by digits. For example, the identifier + {#syntax#}i7{#endsyntax#} refers to a signed 7-bit integer. The maximum allowed bit-width of an + integer type is {#syntax#}65535{#endsyntax#}. +

+ {#see_also|Wrapping Operations#} {#header_close#} {#header_close#} {#header_open|Floats#} @@ -1651,7 +1652,7 @@ test "pointer slicing" { assert(array[3] == 5); } {#code_end#} -

Pointers work at compile-time too, as long as the code does not depend on +

Pointers work at compile-time too, as long as the code does not depend on an undefined memory layout:

{#code_begin|test#} const assert = @import("std").debug.assert; @@ -1694,7 +1695,7 @@ test "comptime @intToPtr" { } } {#code_end#} - {#see_also|Optional Pointers#} + {#see_also|Optional Pointers|@intToPtr|@ptrToInt#} {#header_open|volatile#}

Loads and stores are assumed to not have side effects. If a given load or store should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}. @@ -1823,7 +1824,9 @@ fn foo(bytes: []u8) u32 { } {#code_end#} {#header_close#} + {#see_also|C Pointers|Pointers to Zero Bit Types#} {#header_close#} + {#header_open|Slices#} {#code_begin|test_safety|index out of bounds#} const assert = @import("std").debug.assert; @@ -2043,14 +2046,211 @@ test "linked list" { assert(list2.first.?.data == 1234); } {#code_end#} + + {#header_open|extern struct#} +

An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the + C ABI for the target.

+ {#see_also|extern union|extern enum#} + {#header_close#} + {#header_open|packed struct#} -

{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.

-

TODO bit fields

-

TODO alignment

-

TODO endianness

-

TODO @bitOffsetOf and @byteOffsetOf

-

TODO mention how volatile loads and stores of bit packed fields could be more efficient when - done by hand instead of with packed struct

+

+ Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: +

+ +

+ This means that a {#syntax#}packed struct{#endsyntax#} can participate + in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory. + This even works at {#link|comptime#}: +

+ {#code_begin|test#} +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; + +const Full = packed struct { + number: u16, +}; +const Divided = packed struct { + half1: u8, + quarter3: u4, + quarter4: u4, +}; + +test "@bitCast between packed structs" { + doTheTest(); + comptime doTheTest(); +} + +fn doTheTest() void { + assert(@sizeOf(Full) == 2); + assert(@sizeOf(Divided) == 2); + var full = Full{ .number = 0x1234 }; + var divided = @bitCast(Divided, full); + switch (builtin.endian) { + builtin.Endian.Big => { + assert(divided.half1 == 0x12); + assert(divided.quarter3 == 0x3); + assert(divided.quarter4 == 0x4); + }, + builtin.Endian.Little => { + assert(divided.half1 == 0x34); + assert(divided.quarter3 == 0x2); + assert(divided.quarter4 == 0x1); + }, + } +} + {#code_end#} +

+ Zig allows the address to be taken of a non-byte-aligned field: +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +var foo = BitField{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "pointer to non-byte-aligned field" { + const ptr = &foo.b; + assert(ptr.* == 2); +} + {#code_end#} +

+ However, the pointer to a non-byte-aligned field has special properties and cannot + be passed when a normal pointer is expected: +

+ {#code_begin|test_err|expected type#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +var bit_field = BitField{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "pointer to non-bit-aligned field" { + assert(bar(&bit_field.b) == 2); +} + +fn bar(x: *const u3) u3 { + return x.*; +} + {#code_end#} +

+ In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer + to the non-byte-aligned field mentions the bit offset, but the function expects a byte-aligned pointer. +

+

+ Pointers to non-byte-aligned fields share the same address as the other fields within their host integer: +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +var bit_field = BitField{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "pointer to non-bit-aligned field" { + assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b)); + assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c)); +} + {#code_end#} +

+ This can be observed with {#link|@bitOffsetOf#} and {#link|byteOffsetOf#}: +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +test "pointer to non-bit-aligned field" { + comptime { + assert(@bitOffsetOf(BitField, "a") == 0); + assert(@bitOffsetOf(BitField, "b") == 3); + assert(@bitOffsetOf(BitField, "c") == 6); + + assert(@byteOffsetOf(BitField, "a") == 0); + assert(@byteOffsetOf(BitField, "b") == 0); + assert(@byteOffsetOf(BitField, "c") == 0); + } +} + {#code_end#} +

+ Packed structs have 1-byte alignment. However if you have an overaligned pointer to a packed struct, + Zig should correctly understand the alignment of fields. However there is + a bug: +

+ {#code_begin|test_err#} +const S = packed struct { + a: u32, + b: u32, +}; +test "overaligned pointer to packed struct" { + var foo: S align(4) = undefined; + const ptr: *align(4) S = &foo; + const ptr_to_b: *u32 = &ptr.b; +} + {#code_end#} +

When this bug is fixed, the above test in the documentation will unexpectedly pass, which will + cause the test suite to fail, notifying the bug fixer to update these docs. +

+

+ It's also + planned to be able to set alignment of struct fields. +

+

+ Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future. + For details on this subscribe to + this issue. + TODO update these docs with a recommendation on how to use packed structs with MMIO + (the use case for volatile packed structs) once this issue is resolved. + Don't worry, there will be a good solution for this use case in zig. +

{#header_close#} {#header_open|struct Naming#}

Since all structs are anonymous, Zig infers the type name based on a few rules.

@@ -2200,8 +2400,8 @@ export fn entry(foo: Foo) void { } {#header_close#} {#header_open|packed enum#}

By default, the size of enums is not guaranteed.

-

{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type - of the enum:

+

{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the + integer tag type of the enum:

{#code_begin|test#} const std = @import("std"); @@ -2214,131 +2414,179 @@ test "packed enum" { std.debug.assert(@sizeOf(Number) == @sizeOf(u8)); } {#code_end#} +

This makes the enum eligible to be in a {#link|packed struct#}.

{#header_close#} {#see_also|@memberName|@memberCount|@tagName|@sizeOf#} {#header_close#} {#header_open|union#} - {#code_begin|test|union#} -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -// A union has only 1 active field at a time. +

+ A bare {#syntax#}union{#endsyntax#} defines a set of possible types that a value + can be as a list of fields. Only one field can be active at a time. + The in-memory representation of bare unions is not guaranteed. + Bare unions cannot be used to reinterpret memory. For that, use {#link|@ptrCast#}, + or use an {#link|extern union#} or a {#link|packed union#} which have + guaranteed in-memory layout. + {#link|Accessing the non-active field|Wrong Union Field Access#} is + safety-checked {#link|Undefined Behavior#}: +

+ {#code_begin|test_err|inactive union field#} const Payload = union { Int: i64, Float: f64, Bool: bool, }; test "simple union" { - var payload = Payload {.Int = 1234}; - // payload.Float = 12.34; // ERROR! field not active + var payload = Payload{ .Int = 1234 }; + payload.Float = 12.34; +} + {#code_end#} +

You can activate another field by assigning the entire union:

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const Payload = union { + Int: i64, + Float: f64, + Bool: bool, +}; +test "simple union" { + var payload = Payload{ .Int = 1234 }; assert(payload.Int == 1234); - // You can activate another field by assigning the entire union. - payload = Payload {.Float = 12.34}; + payload = Payload{ .Float = 12.34 }; assert(payload.Float == 12.34); } + {#code_end#} +

+ In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}. +

-// Unions can be given an enum tag type: -const ComplexTypeTag = enum { Ok, NotOk }; + {#header_open|Tagged union#} +

Unions can be declared with an enum tag type. + This turns the union into a tagged union, which makes it eligible + to use with {#link|switch#} expressions. One can use {#link|@TagType#} to + obtain the enum type from the union type. +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const ComplexTypeTag = enum { + Ok, + NotOk, +}; const ComplexType = union(ComplexTypeTag) { Ok: u8, NotOk: void, }; -// Declare a specific instance of the union variant. -test "declare union value" { - const c = ComplexType { .Ok = 0 }; +test "switch on tagged union" { + const c = ComplexType{ .Ok = 42 }; assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); + + switch (c) { + ComplexTypeTag.Ok => |value| assert(value == 42), + ComplexTypeTag.NotOk => unreachable, + } } -// @TagType can be used to access the enum tag type of a union. test "@TagType" { assert(@TagType(ComplexType) == ComplexTypeTag); } + {#code_end#} +

In order to modify the payload of a tagged union in a switch expression, + place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer: +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; -// Unions can be made to infer the enum tag type. -const Foo = union(enum) { - String: []const u8, - Number: u64, - - // void can be omitted when inferring enum tag type. - None, +const ComplexTypeTag = enum { + Ok, + NotOk, +}; +const ComplexType = union(ComplexTypeTag) { + Ok: u8, + NotOk: void, }; -test "union variant switch" { - const p = Foo { .Number = 54 }; - const what_is_it = switch (p) { - // Capture by reference - Foo.String => |*x| blk: { - break :blk "this is a string"; - }, - // Capture by value - Foo.Number => |x| blk: { - assert(x == 54); - break :blk "this is a number"; - }, +test "modify tagged union in switch" { + var c = ComplexType{ .Ok = 42 }; + assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); - Foo.None => blk: { - break :blk "this is a none"; - }, - }; - assert(mem.eql(u8, what_is_it, "this is a number")); + switch (c) { + ComplexTypeTag.Ok => |*value| value.* += 1, + ComplexTypeTag.NotOk => unreachable, + } + + assert(c.Ok == 43); } - -// Unions can have methods just like structs and enums: + {#code_end#} +

+ Unions can be made to infer the enum tag type. + Further, unions can have methods just like structs and enums. +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; const Variant = union(enum) { Int: i32, Bool: bool, + // void can be omitted when inferring enum tag type. + None, + fn truthy(self: Variant) bool { return switch (self) { Variant.Int => |x_int| x_int != 0, Variant.Bool => |x_bool| x_bool, + Variant.None => false, }; } }; test "union method" { - var v1 = Variant { .Int = 1 }; - var v2 = Variant { .Bool = false }; + var v1 = Variant{ .Int = 1 }; + var v2 = Variant{ .Bool = false }; assert(v1.truthy()); assert(!v2.truthy()); } + {#code_end#} +

+ {#link|@tagName#} can be used to return a {#link|comptime#} + {#syntax#}[]const u8{#endsyntax#} value representing the field name: +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; - -const Small = union { - A: i32, - B: bool, - C: u8, -}; - -// @memberCount tells how many fields a union has: -test "@memberCount" { - assert(@memberCount(Small) == 3); -} - -// @memberName tells the name of a field in an enum: -test "@memberName" { - assert(mem.eql(u8, @memberName(Small, 1), "B")); -} - -// @tagName gives a []const u8 representation of an enum value, -// but only if the union has an enum tag type. const Small2 = union(enum) { A: i32, B: bool, C: u8, }; test "@tagName" { - assert(mem.eql(u8, @tagName(Small2.C), "C")); + assert(std.mem.eql(u8, @tagName(Small2.C), "C")); } {#code_end#} -

- Unions with an enum tag are generated as a struct with a tag field and union field. Zig - sorts the order of the tag and union field by the largest alignment. -

{#header_close#} + + {#header_open|extern union#} +

+ An {#syntax#}extern union{#endsyntax#} has memory layout guaranteed to be compatible with + the target C ABI. +

+ {#see_also|extern struct#} + {#header_close#} + + {#header_open|packed union#} +

A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible + to be in a {#link|packed struct#}. + {#header_close#} + {#header_close#} + {#header_open|blocks#}

Blocks are used to limit the scope of variable declarations: @@ -2371,7 +2619,36 @@ test "labeled break from labeled block expression" { {#code_end#}

Here, {#syntax#}blk{#endsyntax#} can be any name.

{#see_also|Labeled while|Labeled for#} + + {#header_open|Shadowing#} +

It is never allowed for an identifier to "hide" another one by using the same name:

+ {#code_begin|test_err|redefinition#} +const pi = 3.14; + +test "inside test block" { + // Let's even go inside another block + { + var pi: i32 = 1234; + } +} + {#code_end#} +

+ Because of this, when you read Zig code you can rely on an identifier always meaning the same thing, + within the scope it is defined. Note that you can, however use the same name if the scopes are separate: +

+ {#code_begin|test#} +test "separate scopes" { + { + const pi = 3.14; + } + { + var pi: bool = true; + } +} + {#code_end#} {#header_close#} + {#header_close#} + {#header_open|switch#} {#code_begin|test|switch#} const assert = @import("std").debug.assert; @@ -2392,7 +2669,7 @@ test "switch simple" { // Ranges can be specified using the ... syntax. These are inclusive // both ends. - 5 ... 100 => 1, + 5...100 => 1, // Branches can be arbitrarily complex. 101 => blk: { @@ -2418,34 +2695,6 @@ test "switch simple" { assert(b == 1); } -test "switch enum" { - const Item = union(enum) { - A: u32, - C: struct { x: u8, y: u8 }, - D, - }; - - var a = Item { .A = 3 }; - - // Switching on more complex enums is allowed. - const b = switch (a) { - // A capture group is allowed on a match, and will return the enum - // value matched. - Item.A => |item| item, - - // A reference to the matched value can be obtained using `*` syntax. - Item.C => |*item| blk: { - item.*.x += 1; - break :blk 6; - }, - - // No else is required if the types cases was exhaustively handled - Item.D => 8, - }; - - assert(b == 3); -} - // Switch expressions can be used outside a function: const os_msg = switch (builtin.os) { builtin.Os.linux => "we found a linux user", @@ -2464,6 +2713,48 @@ test "switch inside function" { }, else => {}, } +} + {#code_end#} +

+ {#syntax#}switch{#endsyntax#} can be used to capture the field values + of a {#link|Tagged union#}. Modifications to the field values can be + done by placing a {#syntax#}*{#endsyntax#} before the capture variable name, + turning it into a pointer. +

+ {#code_begin|test#} +const assert = @import("std").debug.assert; + +test "switch on tagged union" { + const Point = struct { + x: u8, + y: u8, + }; + const Item = union(enum) { + A: u32, + C: Point, + D, + }; + + var a = Item{ .C = Point{ .x = 1, .y = 2 } }; + + // Switching on more complex enums is allowed. + const b = switch (a) { + // A capture group is allowed on a match, and will return the enum + // value matched. + Item.A => |item| item, + + // A reference to the matched value can be obtained using `*` syntax. + Item.C => |*item| blk: { + item.*.x += 1; + break :blk 6; + }, + + // No else is required if the types cases was exhaustively handled + Item.D => 8, + }; + + assert(b == 6); + assert(a.C.x == 2); } {#code_end#} {#see_also|comptime|enum|@compileError|Compile Variables#} @@ -3134,7 +3425,6 @@ const assert = @import("std").debug.assert; // Functions are declared like this fn add(a: i8, b: i8) i8 { if (a == 0) { - // You can still return manually if needed. return b; } @@ -3158,12 +3448,18 @@ fn abort() noreturn { while (true) {} } -// nakedcc makes a function not have any function prologue or epilogue. +// The nakedcc specifier makes a function not have any function prologue or epilogue. // This can be useful when integrating with assembly. nakedcc fn _start() noreturn { abort(); } +// The inline specifier forces a function to be inlined at all call sites. +// If the function cannot be inlined, it is a compile-time error. +inline fn shiftLeftOne(a: u32) u32 { + return a << 1; +} + // The pub specifier allows the function to be visible when importing. // Another file can use @import and call sub2 pub fn sub2(a: i8, b: i8) i8 { return a - b; } @@ -3731,7 +4027,7 @@ fn bang2() void { Here, the stack trace does not explain how the control flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call. One would have to open a debugger or further instrument the application - in order to find out. The error return trace, on the other hand, + in order to find out. The error return trace, on the other hand, shows exactly how the error bubbled up.

@@ -3923,7 +4219,7 @@ test "optional type" { cast it to a different type:

{#code_begin|syntax#} -const optional_value: ?i32 = null; +const optional_value: ?i32 = null; {#code_end#} {#header_close#} {#header_open|Optional Pointers#} @@ -3981,7 +4277,7 @@ test "implicit cast - invoke a type as a function" { {#code_end#}

Implicit casts are only allowed when it is completely unambiguous how to get from one type to another, - and the transformation is guaranteed to be safe. + and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.

{#header_open|Implicit Cast: Stricter Qualification#}

@@ -4228,9 +4524,20 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { {#header_close#} {#header_close#} - {#header_open|void#} + {#header_open|Zero Bit Types#} +

For some types, {#link|@sizeOf#} is 0:

+

- {#syntax#}void{#endsyntax#} represents a type that has no value. Code that makes use of void values is + These types can only ever have one possible value, and thus + require 0 bits to represent. Code that makes use of these types is not included in the final generated code:

{#code_begin|syntax#} @@ -4240,8 +4547,8 @@ export fn entry() void { x = y; } {#code_end#} -

When this turns into LLVM IR, there is no code generated in the body of {#syntax#}entry{#endsyntax#}, - even in debug mode. For example, on x86_64:

+

When this turns into machine code, there is no code generated in the + body of {#syntax#}entry{#endsyntax#}, even in {#link|Debug#} mode. For example, on x86_64:

0000000000000010 <entry>:
   10:	55                   	push   %rbp
   11:	48 89 e5             	mov    %rsp,%rbp
@@ -4249,6 +4556,8 @@ export fn entry() void {
   15:	c3                   	retq   

These assembly instructions do not have any code associated with the void values - they only perform the function call prologue and epilog.

+ + {#header_open|void#}

{#syntax#}void{#endsyntax#} can be useful for instantiating generic types. For example, given a {#syntax#}Map(Key, Value){#endsyntax#}, one can pass {#syntax#}void{#endsyntax#} for the {#syntax#}Value{#endsyntax#} @@ -4320,6 +4629,38 @@ fn foo() i32 { {#code_end#} {#header_close#} + {#header_open|Pointers to Zero Bit Types#} +

Pointers to zero bit types also have zero bits. They always compare equal to each other:

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +test "pointer to empty struct" { + const Empty = struct {}; + var a = Empty{}; + var b = Empty{}; + var ptr_a = &a; + var ptr_b = &b; + comptime assert(ptr_a == ptr_b); +} + {#code_end#} +

The type being pointed to can only ever be one value; therefore loads and stores are + never generated. {#link|ptrToInt#} and {#link|intToPtr#} are not allowed:

+ {#code_begin|test_err#} +const Empty = struct {}; + +test "@ptrToInt for pointer to zero bit type" { + var a = Empty{}; + _ = @ptrToInt(&a); +} + +test "@intToPtr for pointer to zero bit type" { + _ = @intToPtr(*Empty, 0x1); +} + {#code_end#} + {#header_close#} + {#header_close#} + {#header_open|comptime#}

Zig places importance on the concept of whether an expression is known at compile-time. @@ -5101,7 +5442,7 @@ async fn testResumeFromSuspend(my_result: *i32) void {

{#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes as an operand a promise handle. - If the async function associated with the promise handle has already returned, + If the async function associated with the promise handle has already returned, then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value. Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its promise handle with the target coroutine. It becomes the target coroutine's responsibility @@ -5185,7 +5526,7 @@ fn seq(c: u8) void { {#header_close#} - + {#header_close#} {#header_open|Builtin Functions#}

@@ -5540,13 +5881,13 @@ const warn = @import("std").debug.warn; const num1 = blk: { var val1: i32 = 99; - @compileLog("comptime val1 = ", val1); + @compileLog("comptime val1 = ", val1); val1 = val1 + 1; break :blk val1; }; test "main" { - @compileLog("comptime in main"); + @compileLog("comptime in main"); warn("Runtime in main, num1 = {}.\n", num1); } @@ -5556,10 +5897,10 @@ test "main" { will ouput:

- If all {#syntax#}@compileLog{#endsyntax#} calls are removed or + If all {#syntax#}@compileLog{#endsyntax#} calls are removed or not encountered by analysis, the program compiles successfully and the generated executable prints: -

+

{#code_begin|test#} const warn = @import("std").debug.warn; @@ -5658,12 +5999,13 @@ test "main" { {#header_close#} {#header_open|@enumToInt#} -
{#syntax#}@enumToInt(enum_value: var) var{#endsyntax#}
+
{#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}

- Converts an enumeration value into its integer tag type. + Converts an enumeration value into its integer tag type. When a tagged union is passed, + the tag value is used as the enumeration value.

- If the enum has only 1 possible value, the resut is a {#syntax#}comptime_int{#endsyntax#} + If there is only one possible enum value, the resut is a {#syntax#}comptime_int{#endsyntax#} known at {#link|comptime#}.

{#see_also|@intToEnum#} @@ -6104,6 +6446,10 @@ test "call foo" {

Converts a pointer of one type to a pointer of another type.

+

+ {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} + to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. +

{#header_close#} {#header_open|@ptrToInt#} @@ -6293,10 +6639,15 @@ pub const FloatMode = enum {
{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}

This function returns the number of bytes it takes to store {#syntax#}T{#endsyntax#} in memory. -

-

The result is a target-specific compile time constant.

+

+ 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 {#link|integer|Integers#}, + consider whether you want to use {#syntax#}@sizeOf(T){#endsyntax#} or + {#syntax#}@typeInfo(T).Int.bits{#endsyntax#}. +

+ {#see_also|@typeInfo#} {#header_close#} {#header_open|@sliceToBytes#} @@ -6375,7 +6726,7 @@ fn List(comptime T: type) type {

When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the current import. There is a proposal to remove the import type and use an empty struct - type instead. See + type instead. See #1047 for details.

{#header_close#} @@ -6730,11 +7081,7 @@ pub fn build(b: *Builder) void { {#header_open|Single Threaded Builds#}

Zig has a compile option --single-threaded which has the following effects: