diff --git a/doc/docgen.zig b/doc/docgen.zig index f3bb7df6b1..d21ed78cb0 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1,10 +1,14 @@ const std = @import("std"); const io = std.io; const os = std.os; +const warn = std.debug.warn; +const mem = std.mem; + +pub const max_doc_file_size = 10 * 1024 * 1024; pub fn main() -> %void { // TODO use a more general purpose allocator here - var inc_allocator = try std.heap.IncrementingAllocator.init(5 * 1024 * 1024); + var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size); defer inc_allocator.deinit(); const allocator = &inc_allocator.allocator; @@ -25,39 +29,383 @@ pub fn main() -> %void { defer out_file.close(); var file_in_stream = io.FileInStream.init(&in_file); - var buffered_in_stream = io.BufferedInStream.init(&file_in_stream.stream); + + const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size); var file_out_stream = io.FileOutStream.init(&out_file); var buffered_out_stream = io.BufferedOutStream.init(&file_out_stream.stream); - gen(&buffered_in_stream.stream, &buffered_out_stream.stream); - try buffered_out_stream.flush(); + const toc = try genToc(allocator, in_file_name, input_file_bytes); + try genHtml(allocator, toc, &buffered_out_stream.stream); + try buffered_out_stream.flush(); } -const State = enum { - Start, - Derp, +const Token = struct { + id: Id, + start: usize, + end: usize, + + const Id = enum { + Invalid, + Content, + BracketOpen, + TagContent, + Separator, + BracketClose, + Eof, + }; }; -// TODO look for code segments +const Tokenizer = struct { + buffer: []const u8, + index: usize, + state: State, + source_file_name: []const u8, -fn gen(in: &io.InStream, out: &io.OutStream) { - var state = State.Start; - while (true) { - const byte = in.readByte() catch |err| { - if (err == error.EndOfStream) { - return; - } - std.debug.panic("{}", err); + const State = enum { + Start, + LBracket, + Hash, + TagName, + Eof, + }; + + fn init(source_file_name: []const u8, buffer: []const u8) -> Tokenizer { + return Tokenizer { + .buffer = buffer, + .index = 0, + .state = State.Start, + .source_file_name = source_file_name, }; - switch (state) { - State.Start => switch (byte) { - else => { - out.writeByte(byte) catch unreachable; + } + + fn next(self: &Tokenizer) -> Token { + var result = Token { + .id = Token.Id.Eof, + .start = self.index, + .end = undefined, + }; + while (self.index < self.buffer.len) : (self.index += 1) { + const c = self.buffer[self.index]; + switch (self.state) { + State.Start => switch (c) { + '{' => { + self.state = State.LBracket; + }, + else => { + result.id = Token.Id.Content; + }, }, - }, - State.Derp => unreachable, + State.LBracket => switch (c) { + '#' => { + if (result.id != Token.Id.Eof) { + self.index -= 1; + self.state = State.Start; + break; + } else { + result.id = Token.Id.BracketOpen; + self.index += 1; + self.state = State.TagName; + break; + } + }, + else => { + result.id = Token.Id.Content; + self.state = State.Start; + }, + }, + State.TagName => switch (c) { + '|' => { + if (result.id != Token.Id.Eof) { + break; + } else { + result.id = Token.Id.Separator; + self.index += 1; + break; + } + }, + '#' => { + self.state = State.Hash; + }, + else => { + result.id = Token.Id.TagContent; + }, + }, + State.Hash => switch (c) { + '}' => { + if (result.id != Token.Id.Eof) { + self.index -= 1; + self.state = State.TagName; + break; + } else { + result.id = Token.Id.BracketClose; + self.index += 1; + self.state = State.Start; + break; + } + }, + else => { + result.id = Token.Id.TagContent; + self.state = State.TagName; + }, + }, + State.Eof => unreachable, + } + } else { + switch (self.state) { + State.Start, State.LBracket, State.Eof => {}, + else => { + result.id = Token.Id.Invalid; + }, + } + self.state = State.Eof; } + result.end = self.index; + return result; + } + + const Location = struct { + line: usize, + column: usize, + line_start: usize, + line_end: usize, + }; + + fn getTokenLocation(self: &Tokenizer, token: &const Token) -> Location { + var loc = Location { + .line = 0, + .column = 0, + .line_start = 0, + .line_end = 0, + }; + for (self.buffer) |c, i| { + if (i == token.start) { + loc.line_end = i; + while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {} + return loc; + } + if (c == '\n') { + loc.line += 1; + loc.column = 0; + loc.line_start = i + 1; + } else { + loc.column += 1; + } + } + return loc; + } +}; + +error ParseError; + +fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) -> error { + const loc = tokenizer.getTokenLocation(token); + warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args); + if (loc.line_start <= loc.line_end) { + warn("{}\n", tokenizer.buffer[loc.line_start..loc.line_end]); + { + var i: usize = 0; + while (i < loc.column) : (i += 1) { + warn(" "); + } + } + { + const caret_count = token.end - token.start; + var i: usize = 0; + while (i < caret_count) : (i += 1) { + warn("~"); + } + } + warn("\n"); + } + return error.ParseError; +} + +fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) -> %void { + if (token.id != id) { + return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } } + +fn eatToken(tokenizer: &Tokenizer, id: Token.Id) -> %Token { + const token = tokenizer.next(); + try assertToken(tokenizer, token, id); + return token; +} + +const HeaderOpen = struct { + name: []const u8, + url: []const u8, + n: usize, +}; + +const Tag = enum { + Nav, + HeaderOpen, + HeaderClose, +}; + +const Node = union(enum) { + Content: []const u8, + Nav, + HeaderOpen: HeaderOpen, +}; + +const Toc = struct { + nodes: []Node, + toc: []u8, +}; + +const Action = enum { + Open, + Close, +}; + +fn genToc(allocator: &mem.Allocator, source_file_name: []const u8, input_file_bytes: []const u8) -> %Toc { + var tokenizer = Tokenizer.init(source_file_name, input_file_bytes); + + var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator); + defer urls.deinit(); + + var header_stack_size: usize = 0; + var last_action = Action.Open; + + var toc_buf = try std.Buffer.initSize(allocator, 0); + defer toc_buf.deinit(); + + var toc_buf_adapter = io.BufferOutStream.init(&toc_buf); + var toc = &toc_buf_adapter.stream; + + var nodes = std.ArrayList(Node).init(allocator); + defer nodes.deinit(); + + try toc.writeByte('\n'); + + while (true) { + const token = tokenizer.next(); + switch (token.id) { + Token.Id.Eof => { + if (header_stack_size != 0) { + return parseError(&tokenizer, token, "unbalanced headers"); + } + try toc.write(" \n"); + break; + }, + Token.Id.Content => { + try nodes.append(Node {.Content = input_file_bytes[token.start..token.end] }); + }, + Token.Id.BracketOpen => { + const tag_token = try eatToken(&tokenizer, Token.Id.TagContent); + const tag_name = input_file_bytes[tag_token.start..tag_token.end]; + + var tag: Tag = undefined; + if (mem.eql(u8, tag_name, "nav")) { + tag = Tag.Nav; + } else if (mem.eql(u8, tag_name, "header_open")) { + tag = Tag.HeaderOpen; + header_stack_size += 1; + } else if (mem.eql(u8, tag_name, "header_close")) { + if (header_stack_size == 0) { + return parseError(&tokenizer, tag_token, "unbalanced close header"); + } + header_stack_size -= 1; + tag = Tag.HeaderClose; + } else { + return parseError(&tokenizer, tag_token, "unrecognized tag name: {}", tag_name); + } + + var tag_content: ?[]const u8 = null; + const maybe_sep = tokenizer.next(); + if (maybe_sep.id == Token.Id.Separator) { + const content_token = try eatToken(&tokenizer, Token.Id.TagContent); + tag_content = input_file_bytes[content_token.start..content_token.end]; + _ = eatToken(&tokenizer, Token.Id.BracketClose); + } else { + try assertToken(&tokenizer, maybe_sep, Token.Id.BracketClose); + } + + switch (tag) { + Tag.HeaderOpen => { + const content = tag_content ?? return parseError(&tokenizer, tag_token, "expected header content"); + const urlized = try urlize(allocator, content); + try nodes.append(Node{.HeaderOpen = HeaderOpen { + .name = content, + .url = urlized, + .n = header_stack_size, + }}); + if (try urls.put(urlized, tag_token)) |other_tag_token| { + parseError(&tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {}; + parseError(&tokenizer, other_tag_token, "other tag here") catch {}; + return error.ParseError; + } + if (last_action == Action.Open) { + try toc.writeByte('\n'); + try toc.writeByteNTimes(' ', header_stack_size * 4); + try toc.write("\n"); + } else { + try toc.write("\n"); + last_action = Action.Close; + } + }, + Tag.Nav => { + try nodes.append(Node.Nav); + }, + } + }, + else => return parseError(&tokenizer, token, "invalid token"), + } + } + + return Toc { + .nodes = nodes.toOwnedSlice(), + .toc = toc_buf.toOwnedSlice(), + }; +} + +fn urlize(allocator: &mem.Allocator, input: []const u8) -> %[]u8 { + var buf = try std.Buffer.initSize(allocator, 0); + defer buf.deinit(); + + var buf_adapter = io.BufferOutStream.init(&buf); + var out = &buf_adapter.stream; + for (input) |c| { + switch (c) { + 'a'...'z', 'A'...'Z', '_', '-' => { + try out.writeByte(c); + }, + ' ' => { + try out.writeByte('-'); + }, + else => {}, + } + } + return buf.toOwnedSlice(); +} + +fn genHtml(allocator: &mem.Allocator, toc: &const Toc, out: &io.OutStream) -> %void { + for (toc.nodes) |node| { + switch (node) { + Node.Content => |data| { + try out.write(data); + }, + Node.Nav => { + try out.write(toc.toc); + }, + Node.HeaderOpen => |info| { + try out.print("{}\n", info.n, info.url, info.name, info.n); + }, + } + } + +} diff --git a/doc/langref.html.in b/doc/langref.html.in index 94bc780959..1ef979b14b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -31,221 +31,10 @@
-

Zig Documentation

+ {#header_open|Introduction#}

Zig is an open-source programming language designed for robustness, optimality, and clarity. @@ -264,7 +53,8 @@ If you search for something specific in this documentation and do not find it, please file an issue or say something on IRC.

-

Hello World

+ {#header_close#} + {#header_open|Hello World#}
const std = @import("std");
 
 pub fn main() -> %void {
@@ -294,7 +84,8 @@ pub fn main() -> %void {
         
  • Errors
  • Root Source File
  • -

    Source Encoding

    + {#header_close#} + {#header_open|Source Encoding#}

    Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.

    Throughout all zig source code (including in comments), some codepoints are never allowed:

      @@ -303,7 +94,8 @@ pub fn main() -> %void {

    The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possbly the last line of the file).

    For some discussion on the rationale behind these design decisions, see issue #663

    -

    Values

    + {#header_close#} + {#header_open|Values#}
    const warn = @import("std").debug.warn;
     const os = @import("std").os;
     const assert = @import("std").debug.assert;
    @@ -373,7 +165,7 @@ value: error.ArgNotFound
     error union 2
     type: %i32
     value: 1234
    -

    Primitive Types

    + {#header_open|Primitive Types#}
    @@ -606,7 +398,8 @@ value: 1234
  • void
  • Errors
  • -

    Primitive Values

    + {#header_close#} + {#header_open|Primitive Values#}
    @@ -638,7 +431,8 @@ value: 1234
  • Nullables
  • this
  • -

    String Literals

    + {#header_close#} + {#header_open|String Literals#}
    const assert = @import("std").debug.assert;
     const mem = @import("std").mem;
     
    @@ -663,7 +457,7 @@ Test 1/1 string literals...OK
  • Arrays
  • Zig Test
  • -

    Escape Sequences

    + {#header_open|Escape Sequences#}
    @@ -711,7 +505,8 @@ Test 1/1 string literals...OK

    Note that the maximum valid Unicode point is 0x10ffff.

    -

    Multiline String Literals

    + {#header_close#} + {#header_open|Multiline String Literals#}

    Multiline string literals have no escapes and can span across multiple lines. To start a multiline string literal, use the \\ token. Just like a comment, @@ -747,7 +542,9 @@ Test 1/1 string literals...OK

    -

    Assignment

    + {#header_close#} + {#header_close#} + {#header_open|Assignment#}

    Use const to assign a value to an identifier:

    const x = 1234;
     
    @@ -798,14 +595,17 @@ test "init with undefined" {
     }
    $ zig test test.zig
     Test 1/1 init with undefined...OK
    -

    Integers

    -

    Integer Literals

    + {#header_close#} + {#header_close#} + {#header_open|Integers#} + {#header_open|Integer Literals#}
    const decimal_int = 98222;
     const hex_int = 0xff;
     const another_hex_int = 0xFF;
     const octal_int = 0o755;
     const binary_int = 0b11110000;
    -

    Runtime Integer Values

    + {#header_close#} + {#header_open|Runtime Integer Values#}

    Integer literals have no size limitation, and if any undefined behavior occurs, the compiler catches it. @@ -833,8 +633,11 @@ const binary_int = 0b11110000;

  • Division By Zero
  • Wrapping Operations
  • -

    Floats

    -

    Float Literals

    + {#header_close#} + {#header_close#} + {#header_open|Floats#} + {#header_close#} + {#header_open|Float Literals#}
    const floating_point = 123.0E+77;
     const another_float = 123.0;
     const yet_another = 123.0e+77;
    @@ -842,7 +645,8 @@ const yet_another = 123.0e+77;
     const hex_floating_point = 0x103.70p-5;
     const another_hex_float = 0x103.70;
     const yet_another_hex_float = 0x103.70P-5;
    -

    Floating Point Operations

    + {#header_close#} + {#header_open|Floating Point Operations#}

    By default floating point operations use Optimized mode, but you can switch to Strict mode on a per-block basis:

    foo.zig

    @@ -881,8 +685,9 @@ strict = 9.765625e-3
  • @setFloatMode
  • Division By Zero
  • -

    Operators

    -

    Table of Operators

    + {#header_close#} + {#header_open|Operators#} + {#header_open|Table of Operators#}
    @@ -1470,7 +1275,8 @@ const ptr = &x;
    -

    Precedence

    + {#header_close#} + {#header_open|Precedence#}
    x() x[] x.y
     !x -x -%x ~x *x &x ?x %x %%x ??x
     x{}
    @@ -1485,7 +1291,9 @@ and
     or
     ?? catch
     = *= /= %= += -= <<= >>= &= ^= |=
    -

    Arrays

    + {#header_close#} + {#header_close#} + {#header_open|Arrays#}
    const assert = @import("std").debug.assert;
     const mem = @import("std").mem;
     
    @@ -1599,7 +1407,8 @@ Test 4/4 array initialization with function calls...OK
  • for
  • Slices
  • -

    Pointers

    + {#header_close#} + {#header_open|Pointers#}
    const assert = @import("std").debug.assert;
     
     test "address of syntax" {
    @@ -1737,7 +1546,7 @@ Test 5/8 volatile...OK
     Test 6/8 nullable pointers...OK
     Test 7/8 pointer casting...OK
     Test 8/8 pointer child type...OK
    -

    Alignment

    + {#header_open|Alignment#}

    Each type has an alignment - a number of bytes such that, when a value of the type is loaded from or stored to memory, @@ -1838,7 +1647,8 @@ Test 1/1 pointer alignment safety...incorrect alignment Tests failed. Use the following command to reproduce the failure: ./test -

    Type Based Alias Analysis

    + {#header_close#} + {#header_open|Type Based Alias Analysis#}

    Zig uses Type Based Alias Analysis (also known as Strict Aliasing) to perform some optimizations. This means that pointers of different types must not alias the same memory, with the exception of u8. Pointers to @@ -1854,7 +1664,9 @@ Tests failed. Use the following command to reproduce the failure:

  • Slices
  • Memory
  • -

    Slices

    + {#header_close#} + {#header_close#} + {#header_open|Slices#}
    const assert = @import("std").debug.assert;
     
     test "basic slices" {
    @@ -1954,7 +1766,8 @@ Test 3/3 slice widening...OK
  • for
  • Arrays
  • -

    struct

    + {#header_close#} + {#header_open|struct#}
    // Declare a struct.
     // Zig gives no guarantees about the order of fields and whether or
     // not there will be padding.
    @@ -2099,7 +1912,8 @@ Test 4/4 linked list...OK
  • comptime
  • @fieldParentPtr
  • -

    enum

    + {#header_close#} + {#header_open|enum#}
    const assert = @import("std").debug.assert;
     const mem = @import("std").mem;
     
    @@ -2216,7 +2030,8 @@ Test 8/8 @tagName...OK
  • @memberCount
  • @tagName
  • -

    union

    + {#header_close#} + {#header_open|union#}
    const assert = @import("std").debug.assert;
     const mem = @import("std").mem;
     
    @@ -2323,7 +2138,8 @@ Test 7/7 @tagName...OK
    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.

    -

    switch

    + {#header_close#} + {#header_open|switch#}
    const assert = @import("std").debug.assert;
     const builtin = @import("builtin");
     
    @@ -2426,7 +2242,8 @@ Test 3/3 switch inside function...OK
  • @compileError
  • Compile Variables
  • -

    while

    + {#header_close#} + {#header_open|while#}
    const assert = @import("std").debug.assert;
     
     test "while basic" {
    @@ -2595,7 +2412,8 @@ Test 8/8 inline while loop...OK
  • comptime
  • unreachable
  • -

    for

    + {#header_close#} + {#header_open|for#}
    const assert = @import("std").debug.assert;
     
     test "for basics" {
    @@ -2696,7 +2514,8 @@ Test 4/4 inline for loop...OK
  • Arrays
  • Slices
  • -

    if

    + {#header_close#} + {#header_open|if#}
    // If expressions have three uses, corresponding to the three types:
     // * bool
     // * ?T
    @@ -2814,7 +2633,8 @@ Test 3/3 if error union...OK
  • Nullables
  • Errors
  • -

    goto

    + {#header_close#} + {#header_open|goto#}
    const assert = @import("std").debug.assert;
     
     test "goto" {
    @@ -2830,7 +2650,7 @@ label:
     Test 1/1 goto...OK
     

    Note that there are plans to remove goto

    -

    defer

    +{{deheader_open:fer}}
    const assert = @import("std").debug.assert;
     const printf = @import("std").io.stdout.printf;
     
    @@ -2920,7 +2740,8 @@ OK
           
    -      

    unreachable

    + {#header_close#} + {#header_open|unreachable#}

    In Debug and ReleaseSafe mode, and when using zig test, unreachable emits a call to panic with the message reached unreachable code. @@ -2930,7 +2751,7 @@ OK will never be hit to perform optimizations. However, zig test even in ReleaseFast mode still emits unreachable as calls to panic.

    -

    Basics

    + {#header_open|Basics#}
    // unreachable is used to assert that control flow will never happen upon a
     // particular location:
     test "basic math" {
    @@ -2974,7 +2795,8 @@ lib/zig/std/special/bootstrap.zig:34:25: 0x0000000000214750 in ??? (test)
     
     Tests failed. Use the following command to reproduce the failure:
     ./test
    -

    At Compile-Time

    + {#header_close#} + {#header_open|At Compile-Time#}
    const assert = @import("std").debug.assert;
     
     comptime {
    @@ -2995,7 +2817,9 @@ test.zig:9:12: error: unreachable code
             
  • Build Mode
  • comptime
  • -

    noreturn

    + {#header_close#} + {#header_close#} + {#header_open|noreturn#}

    noreturn is the type of:

    @@ -3029,7 +2853,8 @@ fn bar() -> %u32 { } const assert = @import("std").debug.assert;
    -

    Functions

    + {#header_close#} + {#header_open|Functions#}
    const assert = @import("std").debug.assert;
     
     // Functions are declared like this
    @@ -3091,7 +2916,7 @@ comptime {
     
     fn foo() { }
    $ zig build-obj test.zig
    -

    Pass-by-value Parameters

    + {#header_open|Pass-by-value Parameters#}

    In Zig, structs, unions, and enums with payloads cannot be passed by value to a function. @@ -3127,7 +2952,9 @@ export fn entry() { the C ABI does allow passing structs and unions by value. So functions which use the C calling convention may pass structs and unions by value.

    -

    Errors

    + {#header_close#} + {#header_close#} + {#header_open|Errors#}

    One of the distinguishing features of Zig is its exception handling strategy.

    @@ -3321,7 +3148,8 @@ pub fn parseU64(buf: []const u8, radix: u8) -> %u64 {
  • if
  • switch
  • -

    Nullables

    + {#header_close#} + {#header_open|Nullables#}

    One area that Zig provides safety without compromising efficiency or readability is with the nullable type. @@ -3415,7 +3243,8 @@ fn doAThing() -> ?&Foo { The optimizer can sometimes make better decisions knowing that pointer arguments cannot be null.

    -

    Casting

    + {#header_close#} + {#header_open|Casting#}

    TODO: explain implicit vs explicit casting

    TODO: resolve peer types builtin

    TODO: truncate builtin

    @@ -3424,24 +3253,27 @@ fn doAThing() -> ?&Foo {

    TODO: ptr to int builtin

    TODO: ptrcast builtin

    TODO: explain number literals vs concrete types

    -

    void

    + {#header_close#} + {#header_open|void#}

    TODO: assigning void has no codegen

    TODO: hashmap with void becomes a set

    TODO: difference between c_void and void

    TODO: void is the default return value of functions

    TODO: functions require assigning the return value

    -

    this

    + {#header_close#} + {#header_open|this#}

    TODO: example of this referring to Self struct

    TODO: example of this referring to recursion function

    TODO: example of this referring to basic block for @setDebugSafety

    -

    comptime

    + {#header_close#} + {#header_open|comptime#}

    Zig places importance on the concept of whether an expression is known at compile-time. There are a few different places this concept is used, and these building blocks are used to keep the language small, readable, and powerful.

    -

    Introducing the Compile-Time Concept

    -

    Compile-Time Parameters

    + {#header_open|Introducing the Compile-Time Concept#} + {#header_open|Compile-Time Parameters#}

    Compile-time parameters is how Zig implements generics. It is compile-time duck typing.

    @@ -3549,7 +3381,8 @@ fn letsTryToCompareBools(a: bool, b: bool) -> bool { This works the same way for switch expressions - they are implicitly inlined when the target expression is compile-time known.

    -

    Compile-Time Variables

    + {#header_close#} + {#header_open|Compile-Time Variables#}

    In Zig, the programmer can label variables as comptime. This guarantees to the compiler that every load and store of the variable is performed at compile-time. Any violation of this results in a @@ -3631,7 +3464,8 @@ fn performFn(start_value: i32) -> i32 { later in this article, allows expressiveness that in other languages requires using macros, generated code, or a preprocessor to accomplish.

    -

    Compile-Time Expressions

    + {#header_close#} + {#header_open|Compile-Time Expressions#}

    In Zig, it matters whether a given expression is known at compile-time or run-time. A programmer can use a comptime expression to guarantee that the expression will be evaluated at compile-time. @@ -3860,7 +3694,9 @@ fn sum(numbers: []i32) -> i32 { we could call the sum function as is with a slice of numbers whose length and values were only known at run-time.

    -

    Generic Data Structures

    + {#header_close#} + {#header_close#} + {#header_open|Generic Data Structures#}

    Zig uses these capabilities to implement generic data structures without introducing any special-case syntax. If you followed along so far, you may already know how to create a @@ -3895,7 +3731,8 @@ fn sum(numbers: []i32) -> i32 { Node refers to itself as a pointer, which is not actually an infinite regression, so it works fine.

    -

    Case Study: printf in Zig

    + {#header_close#} + {#header_open|Case Study: printf in Zig#}

    Putting all of this together, let's seee how printf works in Zig.

    @@ -4045,35 +3882,42 @@ pub fn main(args: [][]u8) -> %void { a macro language or a preprocessor language. It's Zig all the way down.

    TODO: suggestion to not use inline unless necessary

    -

    inline

    + {#header_close#} + {#header_close#} + {#header_open|inline#}

    TODO: inline while

    TODO: inline for

    TODO: suggestion to not use inline unless necessary

    -

    Assembly

    + {#header_close#} + {#header_open|Assembly#}

    TODO: example of inline assembly

    TODO: example of module level assembly

    TODO: example of using inline assembly return value

    TODO: example of using inline assembly assigning values to variables

    -

    Atomics

    + {#header_close#} + {#header_open|Atomics#}

    TODO: @fence()

    TODO: @atomic rmw

    TODO: builtin atomic memory ordering enum

    -

    Builtin Functions

    + {#header_close#} + {#header_open|Builtin Functions#}

    Builtin functions are provided by the compiler and are prefixed with @. The comptime keyword on a parameter means that the parameter must be known at compile time.

    -

    @addWithOverflow

    + {#header_open|@addWithOverflow#}
    @addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool

    Performs *result = a + b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. If no overflow or underflow occurs, returns false.

    -

    @ArgType

    + {#header_close#} + {#header_open|@ArgType#}

    TODO

    -

    @bitCast

    + {#header_close#} + {#header_open|@bitCast#}
    @bitCast(comptime DestType: type, value: var) -> DestType

    Converts a value of one type to another type. @@ -4094,7 +3938,8 @@ pub fn main(args: [][]u8) -> %void {

    Works at compile-time if value is known at compile time. It's a compile error to bitcast a struct to a scalar type of the same size since structs have undefined layout. However if the struct is packed then it works.

    -

    @breakpoint

    + {#header_close#} + {#header_open|@breakpoint#}
    @breakpoint()

    This function inserts a platform-specific debug trap instruction which causes @@ -4104,7 +3949,8 @@ pub fn main(args: [][]u8) -> %void { This function is only valid within function scope.

    -

    @alignCast

    + {#header_close#} + {#header_open|@alignCast#}
    @alignCast(comptime alignment: u29, ptr: var) -> var

    ptr can be &T, fn(), ?&T, @@ -4114,7 +3960,8 @@ pub fn main(args: [][]u8) -> %void {

    A pointer alignment safety check is added to the generated code to make sure the pointer is aligned as promised.

    -

    @alignOf

    + {#header_close#} + {#header_open|@alignOf#}
    @alignOf(comptime T: type) -> (number literal)

    This function returns the number of bytes that this type should be aligned to @@ -4134,7 +3981,8 @@ comptime {

  • Alignment
  • -

    @cDefine

    + {#header_close#} + {#header_open|@cDefine#}
    @cDefine(comptime name: []u8, value)

    This function can only occur inside @cImport. @@ -4159,7 +4007,8 @@ comptime {

  • @cUndef
  • void
  • -

    @cImport

    + {#header_close#} + {#header_open|@cImport#}
    @cImport(expression) -> (namespace)

    This function parses C code and imports the functions, types, variables, and @@ -4177,7 +4026,8 @@ comptime {

  • @cDefine
  • @cUndef
  • -

    @cInclude

    + {#header_close#} + {#header_open|@cInclude#}
    @cInclude(comptime path: []u8)

    This function can only occur inside @cImport. @@ -4193,7 +4043,8 @@ comptime {

  • @cDefine
  • @cUndef
  • -

    @cUndef

    + {#header_close#} + {#header_open|@cUndef#}
    @cUndef(comptime name: []u8)

    This function can only occur inside @cImport. @@ -4209,12 +4060,14 @@ comptime {

  • @cDefine
  • @cInclude
  • -

    @canImplicitCast

    + {#header_close#} + {#header_open|@canImplicitCast#}
    @canImplicitCast(comptime T: type, value) -> bool

    Returns whether a value can be implicitly casted to a given type.

    -

    @clz

    + {#header_close#} + {#header_open|@clz#}
    @clz(x: T) -> U

    This function counts the number of leading zeroes in x which is an integer @@ -4228,7 +4081,8 @@ comptime { If x is zero, @clz returns T.bit_count.

    -

    @cmpxchg

    + {#header_close#} + {#header_open|@cmpxchg#}
    @cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> bool

    This function performs an atomic compare exchange operation. @@ -4242,7 +4096,8 @@ comptime {

  • Compile Variables
  • -

    @compileError

    + {#header_close#} + {#header_open|@compileError#}
    @compileError(comptime msg: []u8)

    This function, when semantically analyzed, causes a compile error with the @@ -4253,7 +4108,8 @@ comptime { using if or switch with compile time constants, and comptime functions.

    -

    @compileLog

    + {#header_close#} + {#header_open|@compileLog#}
    @compileLog(args: ...)

    This function prints the arguments passed to it at compile-time. @@ -4303,7 +4159,7 @@ test.zig:6:2: error: found compile log statement program compiles successfully and the generated executable prints:

    Runtime in main, num1 = 100.
    -

    @ctz

    +{{@ctheader_open:z}}
    @ctz(x: T) -> U

    This function counts the number of trailing zeroes in x which is an integer @@ -4316,7 +4172,8 @@ test.zig:6:2: error: found compile log statement

    If x is zero, @ctz returns T.bit_count.

    -

    @divExact

    + {#header_close#} + {#header_open|@divExact#}
    @divExact(numerator: T, denominator: T) -> T

    Exact division. Caller guarantees denominator != 0 and @@ -4332,7 +4189,8 @@ test.zig:6:2: error: found compile log statement

  • @divFloor
  • @import("std").math.divExact
  • -

    @divFloor

    + {#header_close#} + {#header_open|@divFloor#}
    @divFloor(numerator: T, denominator: T) -> T

    Floored division. Rounds toward negative infinity. For unsigned integers it is @@ -4349,7 +4207,8 @@ test.zig:6:2: error: found compile log statement

  • @divExact
  • @import("std").math.divFloor
  • -

    @divTrunc

    + {#header_close#} + {#header_open|@divTrunc#}
    @divTrunc(numerator: T, denominator: T) -> T

    Truncated division. Rounds toward zero. For unsigned integers it is @@ -4366,7 +4225,8 @@ test.zig:6:2: error: found compile log statement

  • @divExact
  • @import("std").math.divTrunc
  • -

    @embedFile

    + {#header_close#} + {#header_open|@embedFile#}
    @embedFile(comptime path: []const u8) -> [X]u8

    This function returns a compile time constant fixed-size array with length @@ -4380,17 +4240,20 @@ test.zig:6:2: error: found compile log statement

    -

    @export

    + {#header_close#} + {#header_open|@export#}
    @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) -> []const u8

    Creates a symbol in the output object file.

    -

    @tagName

    + {#header_close#} + {#header_open|@tagName#}
    @tagName(value: var) -> []const u8

    Converts an enum value or union value to a slice of bytes representing the name.

    -

    @TagType

    + {#header_close#} + {#header_open|@TagType#}
    @TagType(T: type) -> type

    For an enum, returns the integer type that is used to store the enumeration value. @@ -4398,7 +4261,8 @@ test.zig:6:2: error: found compile log statement

    For a union, returns the enum type that is used to store the tag value.

    -

    @errorName

    + {#header_close#} + {#header_open|@errorName#}
    @errorName(err: error) -> []u8

    This function returns the string representation of an error. If an error @@ -4413,14 +4277,16 @@ test.zig:6:2: error: found compile log statement or all calls have a compile-time known value for err, then no error name table will be generated.

    -

    @errorReturnTrace

    + {#header_close#} + {#header_open|@errorReturnTrace#}
    @errorReturnTrace() -> ?&builtin.StackTrace

    If the binary is built with error return tracing, and this function is invoked in a function that calls a function with an error or error union return type, returns a stack trace object. Otherwise returns `null`.

    -

    @fence

    + {#header_close#} + {#header_open|@fence#}
    @fence(order: AtomicOrder)

    The fence function is used to introduce happens-before edges between operations. @@ -4432,13 +4298,15 @@ test.zig:6:2: error: found compile log statement

    -

    @fieldParentPtr

    + {#header_close#} + {#header_open|@fieldParentPtr#}
    @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
         field_ptr: &T) -> &ParentType

    Given a pointer to a field, returns the base pointer of a struct.

    -

    @frameAddress

    + {#header_close#} + {#header_open|@frameAddress#}
    @frameAddress()

    This function returns the base pointer of the current stack frame. @@ -4451,7 +4319,8 @@ test.zig:6:2: error: found compile log statement

    This function is only valid within function scope.

    -

    @import

    + {#header_close#} + {#header_open|@import#}
    @import(comptime path: []u8) -> (namespace)

    This function finds a zig file corresponding to path and imports all the @@ -4474,7 +4343,8 @@ test.zig:6:2: error: found compile log statement

  • Compile Variables
  • @embedFile
  • -

    @inlineCall

    + {#header_close#} + {#header_open|@inlineCall#}
    @inlineCall(function: X, args: ...) -> Y

    This calls a function, in the same way that invoking an expression with parentheses does: @@ -4493,17 +4363,20 @@ fn add(a: i32, b: i32) -> i32 { a + b }

    -

    @intToPtr

    + {#header_close#} + {#header_open|@intToPtr#}
    @intToPtr(comptime DestType: type, int: usize) -> DestType

    Converts an integer to a pointer. To convert the other way, use @ptrToInt.

    -

    @IntType

    + {#header_close#} + {#header_open|@IntType#}
    @IntType(comptime is_signed: bool, comptime bit_count: u8) -> type

    This function returns an integer type with the given signness and bit count.

    -

    @maxValue

    + {#header_close#} + {#header_open|@maxValue#}
    @maxValue(comptime T: type) -> (number literal)

    This function returns the maximum value of the integer type T. @@ -4511,7 +4384,8 @@ fn add(a: i32, b: i32) -> i32 { a + b }

    The result is a compile time constant.

    -

    @memberCount

    + {#header_close#} + {#header_open|@memberCount#}
    @memberCount(comptime T: type) -> (number literal)

    This function returns the number of enum values in an enum type. @@ -4519,11 +4393,14 @@ fn add(a: i32, b: i32) -> i32 { a + b }

    The result is a compile time constant.

    -

    @memberName

    + {#header_close#} + {#header_open|@memberName#}

    TODO

    -

    @memberType

    + {#header_close#} + {#header_open|@memberType#}

    TODO

    -

    @memcpy

    + {#header_close#} + {#header_open|@memcpy#}
    @memcpy(noalias dest: &u8, noalias source: &const u8, byte_count: usize)

    This function copies bytes from one region of memory to another. dest and @@ -4540,7 +4417,8 @@ fn add(a: i32, b: i32) -> i32 { a + b }

    There is also a standard library function for this:

    const mem = @import("std").mem;
     mem.copy(u8, dest[0...byte_count], source[0...byte_count]);
    -

    @memset

    + {#header_close#} + {#header_open|@memset#}
    @memset(dest: &u8, c: u8, byte_count: usize)

    This function sets a region of memory to c. dest is a pointer. @@ -4556,7 +4434,8 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);

    There is also a standard library function for this:

    const mem = @import("std").mem;
     mem.set(u8, dest, c);
    -

    @minValue

    + {#header_close#} + {#header_open|@minValue#}
    @minValue(comptime T: type) -> (number literal)

    This function returns the minimum value of the integer type T. @@ -4564,7 +4443,8 @@ mem.set(u8, dest, c);

    The result is a compile time constant.

    -

    @mod

    + {#header_close#} + {#header_open|@mod#}
    @mod(numerator: T, denominator: T) -> T

    Modulus division. For unsigned integers this is the same as @@ -4579,14 +4459,16 @@ mem.set(u8, dest, c);

  • @rem
  • @import("std").math.mod
  • -

    @mulWithOverflow

    + {#header_close#} + {#header_open|@mulWithOverflow#}
    @mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool

    Performs *result = a * b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. If no overflow or underflow occurs, returns false.

    -

    @noInlineCall

    + {#header_close#} + {#header_open|@noInlineCall#}
    @noInlineCall(function: var, args: ...) -> var

    This calls a function, in the same way that invoking an expression with parentheses does: @@ -4605,12 +4487,14 @@ fn add(a: i32, b: i32) -> i32 { a + b }

    -

    @offsetOf

    + {#header_close#} + {#header_open|@offsetOf#}
    @offsetOf(comptime T: type, comptime field_name: [] const u8) -> (number literal)

    This function returns the byte offset of a field relative to its containing struct.

    -

    @OpaqueType

    + {#header_close#} + {#header_open|@OpaqueType#}
    @OpaqueType() -> type

    Creates a new type with an unknown size and alignment. @@ -4630,7 +4514,8 @@ export fn foo(w: &Wat) { test.zig:5:9: error: expected type '&Derp', found '&Wat' bar(w); ^ -

    @panic

    + {#header_close#} + {#header_open|@panic#}
    @panic(message: []const u8) -> noreturn

    Invokes the panic handler function. By default the panic handler function @@ -4649,12 +4534,14 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

  • Root Source File
  • -

    @ptrCast

    + {#header_close#} + {#header_open|@ptrCast#}
    @ptrCast(comptime DestType: type, value: var) -> DestType

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

    -

    @ptrToInt

    + {#header_close#} + {#header_open|@ptrToInt#}
    @ptrToInt(value: var) -> usize

    Converts value to a usize which is the address of the pointer. value can be one of these types: @@ -4667,7 +4554,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

    To convert the other way, use @intToPtr

    -

    @rem

    + {#header_close#} + {#header_open|@rem#}
    @rem(numerator: T, denominator: T) -> T

    Remainder division. For unsigned integers this is the same as @@ -4682,7 +4570,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

  • @mod
  • @import("std").math.rem
  • -

    @returnAddress

    + {#header_close#} + {#header_open|@returnAddress#}
    @returnAddress()

    This function returns a pointer to the return address of the current stack @@ -4696,13 +4585,15 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat' This function is only valid within function scope.

    -

    @setDebugSafety

    + {#header_close#} + {#header_open|@setDebugSafety#}
    @setDebugSafety(scope, safety_on: bool)

    Sets whether debug safety checks are on for a given scope.

    -

    @setEvalBranchQuota

    + {#header_close#} + {#header_open|@setEvalBranchQuota#}
    @setEvalBranchQuota(new_quota: usize)

    Changes the maximum number of backwards branches that compile-time code @@ -4737,7 +4628,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

  • comptime
  • -

    @setFloatMode

    + {#header_close#} + {#header_open|@setFloatMode#}
    @setFloatMode(scope, mode: @import("builtin").FloatMode)

    Sets the floating point mode for a given scope. Possible values are: @@ -4768,7 +4660,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

  • Floating Point Operations
  • -

    @setGlobalLinkage

    + {#header_close#} + {#header_open|@setGlobalLinkage#}
    @setGlobalLinkage(global_variable_name, comptime linkage: GlobalLinkage)

    GlobalLinkage can be found with @import("builtin").GlobalLinkage. @@ -4777,12 +4670,14 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

    -

    @setGlobalSection

    + {#header_close#} + {#header_open|@setGlobalSection#}
    @setGlobalSection(global_variable_name, comptime section_name: []const u8) -> bool

    Puts the global variable in the specified section.

    -

    @shlExact

    + {#header_close#} + {#header_open|@shlExact#}
    @shlExact(value: T, shift_amt: Log2T) -> T

    Performs the left shift operation (<<). Caller guarantees @@ -4797,7 +4692,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

  • @shrExact
  • @shlWithOverflow
  • -

    @shlWithOverflow

    + {#header_close#} + {#header_open|@shlWithOverflow#}
    @shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: &T) -> bool

    Performs *result = a << b. If overflow or underflow occurs, @@ -4813,7 +4709,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

  • @shlExact
  • @shrExact
  • -

    @shrExact

    + {#header_close#} + {#header_open|@shrExact#}
    @shrExact(value: T, shift_amt: Log2T) -> T

    Performs the right shift operation (>>). Caller guarantees @@ -4827,7 +4724,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

    -

    @sizeOf

    + {#header_close#} + {#header_open|@sizeOf#}
    @sizeOf(comptime T: type) -> (number literal)

    This function returns the number of bytes it takes to store T in memory. @@ -4835,14 +4733,16 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'

    The result is a target-specific compile time constant.

    -

    @subWithOverflow

    + {#header_close#} + {#header_open|@subWithOverflow#}
    @subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool

    Performs *result = a - b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. If no overflow or underflow occurs, returns false.

    -

    @truncate

    + {#header_close#} + {#header_open|@truncate#}
    @truncate(comptime T: type, integer) -> T

    This function truncates bits from an integer type, resulting in a smaller @@ -4865,7 +4765,8 @@ const b: u8 = @truncate(u8, a); of endianness on the target platform.

    -

    @typeId

    + {#header_close#} + {#header_open|@typeId#}
    @typeId(comptime T: type) -> @import("builtin").TypeId

    Returns which kind of type something is. Possible values: @@ -4898,20 +4799,24 @@ const b: u8 = @truncate(u8, a); Opaque, }; -

    @typeName

    + {#header_close#} + {#header_open|@typeName#}
    @typeName(T: type) -> []u8

    This function returns the string representation of a type.

    -

    @typeOf

    + {#header_close#} + {#header_open|@typeOf#}
    @typeOf(expression) -> type

    This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated.

    -

    Build Mode

    + {#header_close#} + {#header_close#} + {#header_open|Build Mode#}

    Zig has three build modes:

    @@ -4935,21 +4840,23 @@ pub fn build(b: &Builder) {

      -Drelease-safe=(bool)  optimizations on and safety on
       -Drelease-fast=(bool)  optimizations on and safety off
    -

    Debug

    + {#header_open|Debug#}
    $ zig build-exe example.zig
    • Fast compilation speed
    • Safety checks enabled
    • Slow runtime performance
    -

    ReleaseFast

    + {#header_close#} + {#header_open|ReleaseFast#}
    $ zig build-exe example.zig --release-fast
    • Fast runtime performance
    • Safety checks disabled
    • Slow compilation speed
    -

    ReleaseSafe

    + {#header_close#} + {#header_open|ReleaseSafe#}
    $ zig build-exe example.zig --release-safe
    -

    Undefined Behavior

    + {#header_close#} + {#header_close#} + {#header_open|Undefined Behavior#}

    Zig has many instances of undefined behavior. If undefined behavior is detected at compile-time, Zig emits an error. Most undefined behavior that @@ -5000,7 +4909,7 @@ Test 1/1 safety check...reached unreachable code Tests failed. Use the following command to reproduce the failure: ./test -

    Reaching Unreachable Code

    + {#header_open|Reaching Unreachable Code#}

    At compile-time:

    comptime {
         assert(false);
    @@ -5019,7 +4928,8 @@ fn assert(ok: bool) {
     comptime {
              ^

    At runtime crashes with the message reached unreachable code and a stack trace.

    -

    Index out of Bounds

    + {#header_close#} + {#header_open|Index out of Bounds#}

    At compile-time:

    comptime {
         const array = "hello";
    @@ -5030,7 +4940,8 @@ comptime {
         const garbage = array[5];
                              ^

    At runtime crashes with the message index out of bounds and a stack trace.

    -

    Cast Negative Number to Unsigned Integer

    + {#header_close#} + {#header_open|Cast Negative Number to Unsigned Integer#}

    At compile-time:

    comptime {
         const value: i32 = -1;
    @@ -5044,7 +4955,8 @@ comptime {
           If you are trying to obtain the maximum value of an unsigned integer, use @maxValue(T),
           where T is the integer type, such as u32.
           

    -

    Cast Truncates Data

    + {#header_close#} + {#header_open|Cast Truncates Data#}

    At compile-time:

    comptime {
         const spartan_count: u16 = 300;
    @@ -5060,8 +4972,9 @@ test.zig:3:20: error: cast from 'u16' to 'u8' truncates bits
           where T is the integer type, such as u32, and value
           is the value you want to truncate.
           

    -

    Integer Overflow

    -

    Default Operations

    + {#header_close#} + {#header_open|Integer Overflow#} + {#header_open|Default Operations#}

    The following operators can cause integer overflow:

    • + (addition)
    • @@ -5083,7 +4996,8 @@ test.zig:3:20: error: cast from 'u16' to 'u8' truncates bits byte += 1; ^

    At runtime crashes with the message integer overflow and a stack trace.

    -

    Standard Library Math Functions

    + {#header_close#} + {#header_open|Standard Library Math Functions#}

    These functions provided by the standard library return possible errors.

    • @import("std").math.add
    • @@ -5112,7 +5026,8 @@ pub fn main() -> %void {
      $ zig build-exe test.zig
       $ ./test
       unable to add one: Overflow
      -

      Builtin Overflow Functions

      + {#header_close#} + {#header_open|Builtin Overflow Functions#}

      These builtins return a bool of whether or not overflow occurred, as well as returning the overflowed bits: @@ -5140,7 +5055,8 @@ pub fn main() -> %void {

      $ zig build-exe test.zig
       $ ./test
       overflowed result: 9
      -

      Wrapping Operations

      + {#header_close#} + {#header_open|Wrapping Operations#}

      These operations have guaranteed wraparound semantics.

      @@ -5159,7 +5075,9 @@ test "wraparound addition and subtraction" { const max_val = min_val -% 1; assert(max_val == @maxValue(i32)); }
    -

    Exact Left Shift Overflow

    + {#header_close#} + {#header_close#} + {#header_open|Exact Left Shift Overflow#}

    At compile-time:

    comptime {
         const x = @shlExact(u8(0b01010101), 2);
    @@ -5169,7 +5087,8 @@ test "wraparound addition and subtraction" {
         const x = @shlExact(u8(0b01010101), 2);
                   ^

    At runtime crashes with the message left shift overflowed bits and a stack trace.

    -

    Exact Right Shift Overflow

    + {#header_close#} + {#header_open|Exact Right Shift Overflow#}

    At compile-time:

    comptime {
         const x = @shrExact(u8(0b10101010), 2);
    @@ -5179,7 +5098,8 @@ test "wraparound addition and subtraction" {
         const x = @shrExact(u8(0b10101010), 2);
                   ^

    At runtime crashes with the message right shift overflowed bits and a stack trace.

    -

    Division by Zero

    + {#header_close#} + {#header_open|Division by Zero#}

    At compile-time:

    comptime {
         const a: i32 = 1;
    @@ -5192,7 +5112,8 @@ test "wraparound addition and subtraction" {
                     ^

    At runtime crashes with the message division by zero and a stack trace.

    -

    Remainder Division by Zero

    + {#header_close#} + {#header_open|Remainder Division by Zero#}

    At compile-time:

    comptime {
         const a: i32 = 10;
    @@ -5205,11 +5126,14 @@ test "wraparound addition and subtraction" {
                     ^

    At runtime crashes with the message remainder division by zero and a stack trace.

    -

    Exact Division Remainder

    + {#header_close#} + {#header_open|Exact Division Remainder#}

    TODO

    -

    Slice Widen Remainder

    + {#header_close#} + {#header_open|Slice Widen Remainder#}

    TODO

    -

    Attempt to Unwrap Null

    + {#header_close#} + {#header_open|Attempt to Unwrap Null#}

    At compile-time:

    comptime {
         const nullable_number: ?i32 = null;
    @@ -5235,7 +5159,8 @@ pub fn main() -> %void {
           
    % zig build-exe test.zig
     $ ./test
     it's null
    -

    Attempt to Unwrap Error

    + {#header_close#} + {#header_open|Attempt to Unwrap Error#}

    At compile-time:

    comptime {
         const number = %%getNumberOrFail();
    @@ -5274,7 +5199,8 @@ fn getNumberOrFail() -> %i32 {
     $ ./test
     got error: UnableToReturnNumber
    -

    Invalid Error Code

    + {#header_close#} + {#header_open|Invalid Error Code#}

    At compile-time:

    error AnError;
     comptime {
    @@ -5287,16 +5213,21 @@ comptime {
         const invalid_err = error(number);
                                  ^

    At runtime crashes with the message invalid error code and a stack trace.

    -

    Invalid Enum Cast

    + {#header_close#} + {#header_open|Invalid Enum Cast#}

    TODO

    -

    Incorrect Pointer Alignment

    + {#header_close#} + {#header_open|Incorrect Pointer Alignment#}

    TODO

    -

    Wrong Union Field Access

    + {#header_close#} + {#header_open|Wrong Union Field Access#}

    TODO

    -

    Memory

    + {#header_close#} + {#header_close#} + {#header_open|Memory#}

    TODO: explain no default allocator in zig

    TODO: show how to use the allocator interface

    TODO: mention debug allocator

    @@ -5308,7 +5239,8 @@ comptime {
  • Pointers
  • -

    Compile Variables

    + {#header_close#} + {#header_open|Compile Variables#}

    Compile variables are accessible by importing the "builtin" package, which the compiler makes available to every Zig source file. It contains @@ -5478,7 +5410,8 @@ pub const link_libs = [][]const u8 {

    -

    Root Source File

    + {#header_close#} + {#header_open|Root Source File#}

    TODO: explain how root source file finds other files

    TODO: pub fn main

    TODO: pub fn panic

    @@ -5486,17 +5419,20 @@ pub const link_libs = [][]const u8 {

    TODO: order independent top level declarations

    TODO: lazy analysis

    TODO: using comptime { _ = @import() }

    -

    Zig Test

    + {#header_close#} + {#header_open|Zig Test#}

    TODO: basic usage

    TODO: lazy analysis

    TODO: --test-filter

    TODO: --test-name-prefix

    TODO: testing in releasefast and releasesafe mode. assert still works

    -

    Zig Build System

    + {#header_close#} + {#header_open|Zig Build System#}

    TODO: explain purpose, it's supposed to replace make/cmake

    TODO: example of building a zig executable

    TODO: example of building a C library

    -

    C

    + {#header_close#} + {#header_open|C#}

    Although Zig is independent of C, and, unlike most other languages, does not depend on libc, Zig acknowledges the importance of interacting with existing C code. @@ -5504,7 +5440,7 @@ pub const link_libs = [][]const u8 {

    There are a few ways that Zig facilitates C interop.

    -

    C Type Primitives

    + {#header_open|C Type Primitives#}

    These have guaranteed C ABI compatibility and can be used like any other type.

    @@ -5524,7 +5460,8 @@ pub const link_libs = [][]const u8 { -

    C String Literals

    + {#header_close#} + {#header_open|C String Literals#}
    extern fn puts(&const u8);
     
     pub fn main() -> %void {
    @@ -5539,7 +5476,8 @@ pub fn main() -> %void {
           
    -      

    Import from C Header File

    + {#header_close#} + {#header_open|Import from C Header File#}

    The @cImport builtin function can be used to directly import symbols from .h files: @@ -5574,11 +5512,13 @@ const c = @cImport({

  • @cUndef
  • @import
  • -

    Mixing Object Files

    + {#header_close#} + {#header_open|Mixing Object Files#}

    You can mix Zig object files with any other object files that respect the C ABI. Example:

    -

    base64.zig

    + {#header_close#} + {#header_open|base64.zig#}
    const base64 = @import("std").base64;
     
     export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
    @@ -5592,7 +5532,7 @@ export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
         return decoded_size;
     }
     
    -

    test.c

    +{{teheader_open:st.c}}
    // This header is generated by zig from base64.zig
     #include "base64.h"
     
    @@ -5609,7 +5549,8 @@ int main(int argc, char **argv) {
     
         return 0;
     }
    -

    build.zig

    + {#header_close#} + {#header_open|build.zig#}
    const Builder = @import("std").build.Builder;
     
     pub fn build(b: &Builder) {
    @@ -5625,7 +5566,8 @@ pub fn build(b: &Builder) {
     
         b.default_step.dependOn(&exe.step);
     }
    -

    Terminal

    + {#header_close#} + {#header_open|Terminal#}
    $ zig build
     $ ./test
     all your base are belong to us
    @@ -5634,7 +5576,9 @@ all your base are belong to us
  • Targets
  • Zig Build System
  • -

    Targets

    + {#header_close#} + {#header_close#} + {#header_open|Targets#}

    Zig supports generating code for all targets that LLVM supports. Here is what it looks like to execute zig targets on a Linux x86_64 @@ -5760,14 +5704,15 @@ Environments: Linux x86_64. Not all standard library code requires operating system abstractions, however, so things such as generic data structures work an all above platforms.

    -

    Style Guide

    + {#header_close#} + {#header_open|Style Guide#}

    These coding conventions are not enforced by the compiler, but they are shipped in this documentation along with the compiler in order to provide a point of reference, should anyone wish to point to an authority on agreed upon Zig coding style.

    -

    Whitespace

    + {#header_open|Whitespace#}
    • 4 space indentation @@ -5782,7 +5727,8 @@ coding style. Line length: aim for 100; use common sense.
    -

    Names

    + {#header_close#} + {#header_open|Names#}

    Roughly speaking: camelCaseFunctionName, TitleCaseTypeName, snake_case_variable_name. More precisely: @@ -5816,7 +5762,8 @@ coding style. do what makes sense. For example, if there is an established convention such as ENOENT, follow the established convention.

    -

    Examples

    + {#header_close#} + {#header_open|Examples#}
    const namespace_name = @import("dir_name/file_name.zig");
     var global_var: i32 = undefined;
     const const_name = 42;
    @@ -5858,7 +5805,9 @@ fn readU32Be() -> u32 {}

    See the Zig Standard Library for more examples.

    -

    Grammar

    + {#header_close#} + {#header_close#} + {#header_open|Grammar#}
    Root = many(TopLevelItem) EOF
     
     TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
    @@ -6010,7 +5959,8 @@ KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "u
     ContainerDecl = option("extern" | "packed")
       ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
       "{" many(ContainerMember) "}"
    -

    Zen

    + {#header_close#} + {#header_open|Zen#}
    • Communicate intent precisely.
    • Edge cases matter.
    • @@ -6024,8 +5974,10 @@ ContainerDecl = option("extern" | "packed")
    • Minimize energy spent on coding style.
    • Together we serve end users.
    -

    TODO

    + {#header_close#} + {#header_open|TODO#}

    TODO: document changes from a31b23c46ba2a8c28df01adc1aa0b4d878b9a5cf (compile time reflection additions)

    + {#header_close#} diff --git a/std/hash_map.zig b/std/hash_map.zig index f91e4b31ba..e3c968146e 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -62,8 +62,7 @@ pub fn HashMap(comptime K: type, comptime V: type, .allocator = allocator, .size = 0, .max_distance_from_start_index = 0, - // it doesn't actually matter what we set this to since we use wrapping integer arithmetic - .modification_count = undefined, + .modification_count = if (want_modification_safety) 0 else {}, }; }