authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-22 10:56:49-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-22 10:56:49-05:00
logd0c39895aae085e421ada85d9d07f417d549c7e6
treee21f4cb764cbda0cc6fcc2aaa5e69c83a391794a
parent0c5f8979045ff05e713bb1b7341496012189650f
signaturelock-open Commit is signed but in an unrecognized format.

docs for packed structs

closes #1513

1 files changed, 228 insertions(+), 32 deletions(-)

doc/langref.html.in+228-32
...@@ -8,13 +8,7 @@...@@ -8,13 +8,7 @@
8 body{8 body{
9 background-color:#111;9 background-color:#111;
10 color: #bbb;10 color: #bbb;
11 font-family: system-ui,11 font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
12 /* Fallbacks for browsers that don't support system-ui */
13 /* https://caniuse.com/#search=system-ui */
14 -apple-system, /* iOS and macOS */
15 Roboto, /* Android */
16 "Segoe UI", /* Windows */
17 sans-serif;
18 }12 }
19 a {13 a {
20 color: #88f;14 color: #88f;
...@@ -263,7 +257,7 @@ pub fn main() void {...@@ -263,7 +257,7 @@ pub fn main() void {
263 true and false,257 true and false,
264 true or false,258 true or false,
265 !true);259 !true);
266 260
267 // optional261 // optional
268 var optional_value: ?[]const u8 = null;262 var optional_value: ?[]const u8 = null;
269 assert(optional_value == null);263 assert(optional_value == null);
...@@ -282,7 +276,7 @@ pub fn main() void {...@@ -282,7 +276,7 @@ pub fn main() void {
282276
283 warn("\nerror union 1\ntype: {}\nvalue: {}\n",277 warn("\nerror union 1\ntype: {}\nvalue: {}\n",
284 @typeName(@typeOf(number_or_error)), number_or_error);278 @typeName(@typeOf(number_or_error)), number_or_error);
285 279
286 number_or_error = 1234;280 number_or_error = 1234;
287281
288 warn("\nerror union 2\ntype: {}\nvalue: {}\n",282 warn("\nerror union 2\ntype: {}\nvalue: {}\n",
...@@ -707,15 +701,21 @@ fn divide(a: i32, b: i32) i32 {...@@ -707,15 +701,21 @@ fn divide(a: i32, b: i32) i32 {
707 {#code_end#}701 {#code_end#}
708 <p>702 <p>
709 In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,703 In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,
710 and thus this division operation is vulnerable to both integer overflow and704 and thus this division operation is vulnerable to both {#link|Integer Overflow#} and
711 division by zero.705 {#link|Division by Zero#}.
712 </p>706 </p>
713 <p>707 <p>
714 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on708 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
715 integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and709 integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
716 {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.710 {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
717 </p>711 </p>
718 {#see_also|Integer Overflow|Division by Zero|Wrapping Operations#}712 <p>
713 Zig supports arbitrary bit-width integers, referenced by using
714 an identifier of <code>i</code> or </code>u</code> followed by digits. For example, the identifier
715 {#syntax#}i7{#endsyntax#} refers to a signed 7-bit integer. The maximum allowed bit-width of an
716 integer type is {#syntax#}65535{#endsyntax#}.
717 </p>
718 {#see_also|Wrapping Operations#}
719 {#header_close#}719 {#header_close#}
720 {#header_close#}720 {#header_close#}
721 {#header_open|Floats#}721 {#header_open|Floats#}
...@@ -1652,7 +1652,7 @@ test "pointer slicing" {...@@ -1652,7 +1652,7 @@ test "pointer slicing" {
1652 assert(array[3] == 5);1652 assert(array[3] == 5);
1653}1653}
1654 {#code_end#}1654 {#code_end#}
1655 <p>Pointers work at compile-time too, as long as the code does not depend on 1655 <p>Pointers work at compile-time too, as long as the code does not depend on
1656 an undefined memory layout:</p>1656 an undefined memory layout:</p>
1657 {#code_begin|test#}1657 {#code_begin|test#}
1658const assert = @import("std").debug.assert;1658const assert = @import("std").debug.assert;
...@@ -2047,13 +2047,203 @@ test "linked list" {...@@ -2047,13 +2047,203 @@ test "linked list" {
2047}2047}
2048 {#code_end#}2048 {#code_end#}
2049 {#header_open|packed struct#}2049 {#header_open|packed struct#}
2050 <p>{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.</p>2050 <p>
2051 <p>TODO bit fields</p>2051 Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
2052 <p>TODO alignment</p>2052 </p>
2053 <p>TODO endianness</p>2053 <ul>
2054 <p>TODO @bitOffsetOf and @byteOffsetOf</p>2054 <li>Fields remain in the order declared.</li>
2055 <p>TODO mention how volatile loads and stores of bit packed fields could be more efficient when2055 <li>There is no padding between fields.</li>
2056 done by hand instead of with packed struct</p>2056 <li>Zig supports arbitrary width {#link|Integers#} and although normally, integers with fewer
2057 than 8 bits will still use 1 byte of memory, in packed structs, they use
2058 exactly their bit width.
2059 </li>
2060 <li>{#syntax#}bool{#endsyntax#} fields use exactly 1 bit.</li>
2061 <li>A {#link|packed enum#} field uses exactly the bit width of its integer tag type.</li>
2062 <li>A {#link|packed union#} field uses exactly the bit width of the union field with
2063 the largest bit width.</li>
2064 <li>Non-byte-aligned fields are packed into the smallest possible
2065 byte-aligned integers in accordance with the target endianness.
2066 </li>
2067 </ul>
2068 <p>
2069 This means that a {#syntax#}packed struct{#endsyntax#} can participate
2070 in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.
2071 This even works at {#link|comptime#}:
2072 </p>
2073 {#code_begin|test#}
2074const std = @import("std");
2075const builtin = @import("builtin");
2076const assert = std.debug.assert;
2077
2078const Full = packed struct {
2079 number: u16,
2080};
2081const Divided = packed struct {
2082 half1: u8,
2083 quarter3: u4,
2084 quarter4: u4,
2085};
2086
2087test "@bitCast between packed structs" {
2088 doTheTest();
2089 comptime doTheTest();
2090}
2091
2092fn doTheTest() void {
2093 assert(@sizeOf(Full) == 2);
2094 assert(@sizeOf(Divided) == 2);
2095 var full = Full{ .number = 0x1234 };
2096 var divided = @bitCast(Divided, full);
2097 switch (builtin.endian) {
2098 builtin.Endian.Big => {
2099 assert(divided.half1 == 0x12);
2100 assert(divided.quarter3 == 0x3);
2101 assert(divided.quarter4 == 0x4);
2102 },
2103 builtin.Endian.Little => {
2104 assert(divided.half1 == 0x34);
2105 assert(divided.quarter3 == 0x2);
2106 assert(divided.quarter4 == 0x1);
2107 },
2108 }
2109}
2110 {#code_end#}
2111 <p>
2112 Zig allows the address to be taken of a non-byte-aligned field:
2113 </p>
2114 {#code_begin|test#}
2115const std = @import("std");
2116const assert = std.debug.assert;
2117
2118const BitField = packed struct {
2119 a: u3,
2120 b: u3,
2121 c: u2,
2122};
2123
2124var foo = BitField{
2125 .a = 1,
2126 .b = 2,
2127 .c = 3,
2128};
2129
2130test "pointer to non-byte-aligned field" {
2131 const ptr = &foo.b;
2132 assert(ptr.* == 2);
2133}
2134 {#code_end#}
2135 <p>
2136 However, the pointer to a non-byte-aligned field has special properties and cannot
2137 be passed when a normal pointer is expected:
2138 </p>
2139 {#code_begin|test_err|expected type#}
2140const std = @import("std");
2141const assert = std.debug.assert;
2142
2143const BitField = packed struct {
2144 a: u3,
2145 b: u3,
2146 c: u2,
2147};
2148
2149var bit_field = BitField{
2150 .a = 1,
2151 .b = 2,
2152 .c = 3,
2153};
2154
2155test "pointer to non-bit-aligned field" {
2156 assert(bar(&bit_field.b) == 2);
2157}
2158
2159fn bar(x: *const u3) u3 {
2160 return x.*;
2161}
2162 {#code_end#}
2163 <p>
2164 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer
2165 to the non-byte-aligned field mentions the bit offset, but the function expects a byte-aligned pointer.
2166 </p>
2167 <p>
2168 Pointers to non-byte-aligned fields share the same address as the other fields within their host integer:
2169 </p>
2170 {#code_begin|test#}
2171const std = @import("std");
2172const assert = std.debug.assert;
2173
2174const BitField = packed struct {
2175 a: u3,
2176 b: u3,
2177 c: u2,
2178};
2179
2180var bit_field = BitField{
2181 .a = 1,
2182 .b = 2,
2183 .c = 3,
2184};
2185
2186test "pointer to non-bit-aligned field" {
2187 assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b));
2188 assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c));
2189}
2190 {#code_end#}
2191 <p>
2192 This can be observed with {#link|@bitOffsetOf#} and {#link|byteOffsetOf#}:
2193 </p>
2194 {#code_begin|test#}
2195const std = @import("std");
2196const assert = std.debug.assert;
2197
2198const BitField = packed struct {
2199 a: u3,
2200 b: u3,
2201 c: u2,
2202};
2203
2204test "pointer to non-bit-aligned field" {
2205 comptime {
2206 assert(@bitOffsetOf(BitField, "a") == 0);
2207 assert(@bitOffsetOf(BitField, "b") == 3);
2208 assert(@bitOffsetOf(BitField, "c") == 6);
2209
2210 assert(@byteOffsetOf(BitField, "a") == 0);
2211 assert(@byteOffsetOf(BitField, "b") == 0);
2212 assert(@byteOffsetOf(BitField, "c") == 0);
2213 }
2214}
2215 {#code_end#}
2216 <p>
2217 Packed structs have 1-byte alignment. However if you have an overaligned pointer to a packed struct,
2218 Zig should correctly understand the alignment of fields. However there is
2219 <a href="https://github.com/ziglang/zig/issues/1994">a bug</a>:
2220 </p>
2221 {#code_begin|test_err#}
2222const S = packed struct {
2223 a: u32,
2224 b: u32,
2225};
2226test "overaligned pointer to packed struct" {
2227 var foo: S align(4) = undefined;
2228 const ptr: *align(4) S = &foo;
2229 const ptr_to_b: *u32 = &ptr.b;
2230}
2231 {#code_end#}
2232 <p>When this bug is fixed, the above test in the documentation will unexpectedly pass, which will
2233 cause the test suite to fail, notifying the bug fixer to update these docs.
2234 </p>
2235 <p>
2236 It's also
2237 <a href="https://github.com/ziglang/zig/issues/1512">planned to be able to set alignment of struct fields</a>.
2238 </p>
2239 <p>
2240 Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
2241 For details on this subscribe to
2242 <a href="https://github.com/ziglang/zig/issues/1761">this issue</a>.
2243 TODO update these docs with a recommendation on how to use packed structs with MMIO
2244 (the use case for volatile packed structs) once this issue is resolved.
2245 Don't worry, there will be a good solution for this use case in zig.
2246 </p>
2057 {#header_close#}2247 {#header_close#}
2058 {#header_open|struct Naming#}2248 {#header_open|struct Naming#}
2059 <p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>2249 <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 { }...@@ -2203,8 +2393,8 @@ export fn entry(foo: Foo) void { }
2203 {#header_close#}2393 {#header_close#}
2204 {#header_open|packed enum#}2394 {#header_open|packed enum#}
2205 <p>By default, the size of enums is not guaranteed.</p>2395 <p>By default, the size of enums is not guaranteed.</p>
2206 <p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type2396 <p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the
2207 of the enum:</p>2397 integer tag type of the enum:</p>
2208 {#code_begin|test#}2398 {#code_begin|test#}
2209const std = @import("std");2399const std = @import("std");
22102400
...@@ -2217,6 +2407,7 @@ test "packed enum" {...@@ -2217,6 +2407,7 @@ test "packed enum" {
2217 std.debug.assert(@sizeOf(Number) == @sizeOf(u8));2407 std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
2218}2408}
2219 {#code_end#}2409 {#code_end#}
2410 <p>This makes the enum eligible to be in a {#link|packed struct#}.</p>
2220 {#header_close#}2411 {#header_close#}
2221 {#see_also|@memberName|@memberCount|@tagName|@sizeOf#}2412 {#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
2222 {#header_close#}2413 {#header_close#}
...@@ -2344,7 +2535,12 @@ test "@tagName" {...@@ -2344,7 +2535,12 @@ test "@tagName" {
2344 Unions with an enum tag are generated as a struct with a tag field and union field. Zig2535 Unions with an enum tag are generated as a struct with a tag field and union field. Zig
2345 sorts the order of the tag and union field by the largest alignment.2536 sorts the order of the tag and union field by the largest alignment.
2346 </p>2537 </p>
2538 {#header_open|packed union#}
2539 <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
2540 to be in a {#link|packed struct#}.
2541 {#header_close#}
2347 {#header_close#}2542 {#header_close#}
2543
2348 {#header_open|blocks#}2544 {#header_open|blocks#}
2349 <p>2545 <p>
2350 Blocks are used to limit the scope of variable declarations:2546 Blocks are used to limit the scope of variable declarations:
...@@ -3771,7 +3967,7 @@ fn bang2() void {...@@ -3771,7 +3967,7 @@ fn bang2() void {
3771 Here, the stack trace does not explain how the control3967 Here, the stack trace does not explain how the control
3772 flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.3968 flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.
3773 One would have to open a debugger or further instrument the application3969 One would have to open a debugger or further instrument the application
3774 in order to find out. The error return trace, on the other hand, 3970 in order to find out. The error return trace, on the other hand,
3775 shows exactly how the error bubbled up.3971 shows exactly how the error bubbled up.
3776 </p>3972 </p>
3777 <p>3973 <p>
...@@ -3963,7 +4159,7 @@ test "optional type" {...@@ -3963,7 +4159,7 @@ test "optional type" {
3963 cast it to a different type:4159 cast it to a different type:
3964 </p>4160 </p>
3965 {#code_begin|syntax#}4161 {#code_begin|syntax#}
3966const optional_value: ?i32 = null; 4162const optional_value: ?i32 = null;
3967 {#code_end#}4163 {#code_end#}
3968 {#header_close#}4164 {#header_close#}
3969 {#header_open|Optional Pointers#}4165 {#header_open|Optional Pointers#}
...@@ -5141,7 +5337,7 @@ async fn testResumeFromSuspend(my_result: *i32) void {...@@ -5141,7 +5337,7 @@ async fn testResumeFromSuspend(my_result: *i32) void {
5141 <p>5337 <p>
5142 {#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes5338 {#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes
5143 as an operand a promise handle.5339 as an operand a promise handle.
5144 If the async function associated with the promise handle has already returned, 5340 If the async function associated with the promise handle has already returned,
5145 then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value.5341 then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value.
5146 Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its5342 Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its
5147 promise handle with the target coroutine. It becomes the target coroutine's responsibility5343 promise handle with the target coroutine. It becomes the target coroutine's responsibility
...@@ -5225,7 +5421,7 @@ fn seq(c: u8) void {...@@ -5225,7 +5421,7 @@ fn seq(c: u8) void {
5225 </li>5421 </li>
5226 </ul>5422 </ul>
5227 {#header_close#}5423 {#header_close#}
5228 5424
5229 {#header_close#}5425 {#header_close#}
5230 {#header_open|Builtin Functions#}5426 {#header_open|Builtin Functions#}
5231 <p>5427 <p>
...@@ -5580,13 +5776,13 @@ const warn = @import("std").debug.warn;...@@ -5580,13 +5776,13 @@ const warn = @import("std").debug.warn;
55805776
5581const num1 = blk: {5777const num1 = blk: {
5582 var val1: i32 = 99;5778 var val1: i32 = 99;
5583 @compileLog("comptime val1 = ", val1); 5779 @compileLog("comptime val1 = ", val1);
5584 val1 = val1 + 1;5780 val1 = val1 + 1;
5585 break :blk val1;5781 break :blk val1;
5586};5782};
55875783
5588test "main" {5784test "main" {
5589 @compileLog("comptime in main"); 5785 @compileLog("comptime in main");
55905786
5591 warn("Runtime in main, num1 = {}.\n", num1);5787 warn("Runtime in main, num1 = {}.\n", num1);
5592}5788}
...@@ -5596,10 +5792,10 @@ test "main" {...@@ -5596,10 +5792,10 @@ test "main" {
5596 will ouput:5792 will ouput:
5597 </p>5793 </p>
5598 <p>5794 <p>
5599 If all {#syntax#}@compileLog{#endsyntax#} calls are removed or 5795 If all {#syntax#}@compileLog{#endsyntax#} calls are removed or
5600 not encountered by analysis, the5796 not encountered by analysis, the
5601 program compiles successfully and the generated executable prints:5797 program compiles successfully and the generated executable prints:
5602 </p> 5798 </p>
5603 {#code_begin|test#}5799 {#code_begin|test#}
5604const warn = @import("std").debug.warn;5800const warn = @import("std").debug.warn;
56055801
...@@ -6425,7 +6621,7 @@ fn List(comptime T: type) type {...@@ -6425,7 +6621,7 @@ fn List(comptime T: type) type {
6425 <p>6621 <p>
6426 When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the6622 When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the
6427 current import. There is a proposal to remove the import type and use an empty struct6623 current import. There is a proposal to remove the import type and use an empty struct
6428 type instead. See 6624 type instead. See
6429 <a href="https://github.com/ziglang/zig/issues/1047">#1047</a> for details.6625 <a href="https://github.com/ziglang/zig/issues/1047">#1047</a> for details.
6430 </p>6626 </p>
6431 {#header_close#}6627 {#header_close#}
...@@ -7560,7 +7756,7 @@ const c = @cImport({...@@ -7560,7 +7756,7 @@ const c = @cImport({
7560 {#link|Undefined Behavior#} occurs if the address is 0.7756 {#link|Undefined Behavior#} occurs if the address is 0.
7561 </li>7757 </li>
7562 <li>Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked7758 <li>Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked
7563 {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of 7759 {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of
7564 null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer7760 null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer
7565 is unnecessary as one can use normal {#link|Optional Pointers#}.7761 is unnecessary as one can use normal {#link|Optional Pointers#}.
7566 </li>7762 </li>