diff --git a/doc/langref.html.in b/doc/langref.html.in index 9a286cb06..b0c3a9e91 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; @@ -263,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); @@ -282,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", @@ -707,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#} @@ -1652,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; @@ -2047,13 +2047,203 @@ test "linked list" { } {#code_end#} {#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.

@@ -2203,8 +2393,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"); @@ -2217,6 +2407,7 @@ 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#} @@ -2344,7 +2535,12 @@ test "@tagName" { 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_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: @@ -3771,7 +3967,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.

@@ -3963,7 +4159,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#} @@ -5141,7 +5337,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 @@ -5225,7 +5421,7 @@ fn seq(c: u8) void { {#header_close#} - + {#header_close#} {#header_open|Builtin Functions#}

@@ -5580,13 +5776,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); } @@ -5596,10 +5792,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; @@ -6425,7 +6621,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#} @@ -7560,7 +7756,7 @@ const c = @cImport({ {#link|Undefined Behavior#} occurs if the address is 0.
  • Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked - {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of + {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer is unnecessary as one can use normal {#link|Optional Pointers#}.