parent
0c5f897904
commit
d0c39895aa
|
@ -8,13 +8,7 @@
|
||||||
body{
|
body{
|
||||||
background-color:#111;
|
background-color:#111;
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
font-family: system-ui,
|
font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: #88f;
|
color: #88f;
|
||||||
|
@ -263,7 +257,7 @@ pub fn main() void {
|
||||||
true and false,
|
true and false,
|
||||||
true or false,
|
true or false,
|
||||||
!true);
|
!true);
|
||||||
|
|
||||||
// optional
|
// optional
|
||||||
var optional_value: ?[]const u8 = null;
|
var optional_value: ?[]const u8 = null;
|
||||||
assert(optional_value == null);
|
assert(optional_value == null);
|
||||||
|
@ -282,7 +276,7 @@ pub fn main() void {
|
||||||
|
|
||||||
warn("\nerror union 1\ntype: {}\nvalue: {}\n",
|
warn("\nerror union 1\ntype: {}\nvalue: {}\n",
|
||||||
@typeName(@typeOf(number_or_error)), number_or_error);
|
@typeName(@typeOf(number_or_error)), number_or_error);
|
||||||
|
|
||||||
number_or_error = 1234;
|
number_or_error = 1234;
|
||||||
|
|
||||||
warn("\nerror union 2\ntype: {}\nvalue: {}\n",
|
warn("\nerror union 2\ntype: {}\nvalue: {}\n",
|
||||||
|
@ -707,15 +701,21 @@ fn divide(a: i32, b: i32) i32 {
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
<p>
|
<p>
|
||||||
In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,
|
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
|
and thus this division operation is vulnerable to both {#link|Integer Overflow#} and
|
||||||
division by zero.
|
{#link|Division by Zero#}.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
|
Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
|
||||||
integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
|
integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
|
||||||
{#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
|
{#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
|
||||||
</p>
|
</p>
|
||||||
{#see_also|Integer Overflow|Division by Zero|Wrapping Operations#}
|
<p>
|
||||||
|
Zig supports arbitrary bit-width integers, referenced by using
|
||||||
|
an identifier of <code>i</code> or </code>u</code> 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#}.
|
||||||
|
</p>
|
||||||
|
{#see_also|Wrapping Operations#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|Floats#}
|
{#header_open|Floats#}
|
||||||
|
@ -1652,7 +1652,7 @@ test "pointer slicing" {
|
||||||
assert(array[3] == 5);
|
assert(array[3] == 5);
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
<p>Pointers work at compile-time too, as long as the code does not depend on
|
<p>Pointers work at compile-time too, as long as the code does not depend on
|
||||||
an undefined memory layout:</p>
|
an undefined memory layout:</p>
|
||||||
{#code_begin|test#}
|
{#code_begin|test#}
|
||||||
const assert = @import("std").debug.assert;
|
const assert = @import("std").debug.assert;
|
||||||
|
@ -2047,13 +2047,203 @@ test "linked list" {
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
{#header_open|packed struct#}
|
{#header_open|packed struct#}
|
||||||
<p>{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.</p>
|
<p>
|
||||||
<p>TODO bit fields</p>
|
Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
|
||||||
<p>TODO alignment</p>
|
</p>
|
||||||
<p>TODO endianness</p>
|
<ul>
|
||||||
<p>TODO @bitOffsetOf and @byteOffsetOf</p>
|
<li>Fields remain in the order declared.</li>
|
||||||
<p>TODO mention how volatile loads and stores of bit packed fields could be more efficient when
|
<li>There is no padding between fields.</li>
|
||||||
done by hand instead of with packed struct</p>
|
<li>Zig supports arbitrary width {#link|Integers#} and although normally, integers with fewer
|
||||||
|
than 8 bits will still use 1 byte of memory, in packed structs, they use
|
||||||
|
exactly their bit width.
|
||||||
|
</li>
|
||||||
|
<li>{#syntax#}bool{#endsyntax#} fields use exactly 1 bit.</li>
|
||||||
|
<li>A {#link|packed enum#} field uses exactly the bit width of its integer tag type.</li>
|
||||||
|
<li>A {#link|packed union#} field uses exactly the bit width of the union field with
|
||||||
|
the largest bit width.</li>
|
||||||
|
<li>Non-byte-aligned fields are packed into the smallest possible
|
||||||
|
byte-aligned integers in accordance with the target endianness.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
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#}:
|
||||||
|
</p>
|
||||||
|
{#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#}
|
||||||
|
<p>
|
||||||
|
Zig allows the address to be taken of a non-byte-aligned field:
|
||||||
|
</p>
|
||||||
|
{#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#}
|
||||||
|
<p>
|
||||||
|
However, the pointer to a non-byte-aligned field has special properties and cannot
|
||||||
|
be passed when a normal pointer is expected:
|
||||||
|
</p>
|
||||||
|
{#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#}
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Pointers to non-byte-aligned fields share the same address as the other fields within their host integer:
|
||||||
|
</p>
|
||||||
|
{#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#}
|
||||||
|
<p>
|
||||||
|
This can be observed with {#link|@bitOffsetOf#} and {#link|byteOffsetOf#}:
|
||||||
|
</p>
|
||||||
|
{#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#}
|
||||||
|
<p>
|
||||||
|
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 href="https://github.com/ziglang/zig/issues/1994">a bug</a>:
|
||||||
|
</p>
|
||||||
|
{#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#}
|
||||||
|
<p>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.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
It's also
|
||||||
|
<a href="https://github.com/ziglang/zig/issues/1512">planned to be able to set alignment of struct fields</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
|
||||||
|
For details on this subscribe to
|
||||||
|
<a href="https://github.com/ziglang/zig/issues/1761">this issue</a>.
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|struct Naming#}
|
{#header_open|struct Naming#}
|
||||||
<p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>
|
<p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>
|
||||||
|
@ -2203,8 +2393,8 @@ export fn entry(foo: Foo) void { }
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|packed enum#}
|
{#header_open|packed enum#}
|
||||||
<p>By default, the size of enums is not guaranteed.</p>
|
<p>By default, the size of enums is not guaranteed.</p>
|
||||||
<p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type
|
<p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the
|
||||||
of the enum:</p>
|
integer tag type of the enum:</p>
|
||||||
{#code_begin|test#}
|
{#code_begin|test#}
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
@ -2217,6 +2407,7 @@ test "packed enum" {
|
||||||
std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
|
std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
|
||||||
}
|
}
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
|
<p>This makes the enum eligible to be in a {#link|packed struct#}.</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
|
{#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
|
||||||
{#header_close#}
|
{#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
|
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.
|
sorts the order of the tag and union field by the largest alignment.
|
||||||
</p>
|
</p>
|
||||||
|
{#header_open|packed union#}
|
||||||
|
<p>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_close#}
|
||||||
|
|
||||||
{#header_open|blocks#}
|
{#header_open|blocks#}
|
||||||
<p>
|
<p>
|
||||||
Blocks are used to limit the scope of variable declarations:
|
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
|
Here, the stack trace does not explain how the control
|
||||||
flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.
|
flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.
|
||||||
One would have to open a debugger or further instrument the application
|
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.
|
shows exactly how the error bubbled up.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -3963,7 +4159,7 @@ test "optional type" {
|
||||||
cast it to a different type:
|
cast it to a different type:
|
||||||
</p>
|
</p>
|
||||||
{#code_begin|syntax#}
|
{#code_begin|syntax#}
|
||||||
const optional_value: ?i32 = null;
|
const optional_value: ?i32 = null;
|
||||||
{#code_end#}
|
{#code_end#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|Optional Pointers#}
|
{#header_open|Optional Pointers#}
|
||||||
|
@ -5141,7 +5337,7 @@ async fn testResumeFromSuspend(my_result: *i32) void {
|
||||||
<p>
|
<p>
|
||||||
{#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes
|
{#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes
|
||||||
as an operand a promise handle.
|
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.
|
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
|
Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its
|
||||||
promise handle with the target coroutine. It becomes the target coroutine's responsibility
|
promise handle with the target coroutine. It becomes the target coroutine's responsibility
|
||||||
|
@ -5225,7 +5421,7 @@ fn seq(c: u8) void {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|Builtin Functions#}
|
{#header_open|Builtin Functions#}
|
||||||
<p>
|
<p>
|
||||||
|
@ -5580,13 +5776,13 @@ const warn = @import("std").debug.warn;
|
||||||
|
|
||||||
const num1 = blk: {
|
const num1 = blk: {
|
||||||
var val1: i32 = 99;
|
var val1: i32 = 99;
|
||||||
@compileLog("comptime val1 = ", val1);
|
@compileLog("comptime val1 = ", val1);
|
||||||
val1 = val1 + 1;
|
val1 = val1 + 1;
|
||||||
break :blk val1;
|
break :blk val1;
|
||||||
};
|
};
|
||||||
|
|
||||||
test "main" {
|
test "main" {
|
||||||
@compileLog("comptime in main");
|
@compileLog("comptime in main");
|
||||||
|
|
||||||
warn("Runtime in main, num1 = {}.\n", num1);
|
warn("Runtime in main, num1 = {}.\n", num1);
|
||||||
}
|
}
|
||||||
|
@ -5596,10 +5792,10 @@ test "main" {
|
||||||
will ouput:
|
will ouput:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If all {#syntax#}@compileLog{#endsyntax#} calls are removed or
|
If all {#syntax#}@compileLog{#endsyntax#} calls are removed or
|
||||||
not encountered by analysis, the
|
not encountered by analysis, the
|
||||||
program compiles successfully and the generated executable prints:
|
program compiles successfully and the generated executable prints:
|
||||||
</p>
|
</p>
|
||||||
{#code_begin|test#}
|
{#code_begin|test#}
|
||||||
const warn = @import("std").debug.warn;
|
const warn = @import("std").debug.warn;
|
||||||
|
|
||||||
|
@ -6425,7 +6621,7 @@ fn List(comptime T: type) type {
|
||||||
<p>
|
<p>
|
||||||
When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the
|
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
|
current import. There is a proposal to remove the import type and use an empty struct
|
||||||
type instead. See
|
type instead. See
|
||||||
<a href="https://github.com/ziglang/zig/issues/1047">#1047</a> for details.
|
<a href="https://github.com/ziglang/zig/issues/1047">#1047</a> for details.
|
||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
@ -7560,7 +7756,7 @@ const c = @cImport({
|
||||||
{#link|Undefined Behavior#} occurs if the address is 0.
|
{#link|Undefined Behavior#} occurs if the address is 0.
|
||||||
</li>
|
</li>
|
||||||
<li>Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked
|
<li>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
|
null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer
|
||||||
is unnecessary as one can use normal {#link|Optional Pointers#}.
|
is unnecessary as one can use normal {#link|Optional Pointers#}.
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user