| author | |
| committer | |
| log | 7c5e3e1f8e4671b383865df0150b549e2445d170 |
| tree | 5bc73aa60ecc49695fd9bf8bab550ac1e75657be |
| parent | 5a21d3dce0c020833bfe426049e2391ae3977846 |
| signature |
5 files changed, 104 insertions(+), 63 deletions(-)
doc/langref.html.in+28-12| ... | @@ -1947,6 +1947,15 @@ test "linked list" { | ... | @@ -1947,6 +1947,15 @@ test "linked list" { |
| 1947 | assert(list2.first.?.data == 1234); | 1947 | assert(list2.first.?.data == 1234); |
| 1948 | } | 1948 | } |
| 1949 | {#code_end#} | 1949 | {#code_end#} |
| 1950 | {#header_open|packed struct#} | ||
| 1951 | <p>{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.</p> | ||
| 1952 | <p>TODO bit fields</p> | ||
| 1953 | <p>TODO alignment</p> | ||
| 1954 | <p>TODO endianness</p> | ||
| 1955 | <p>TODO @bitOffsetOf and @byteOffsetOf</p> | ||
| 1956 | <p>TODO mention how volatile loads and stores of bit packed fields could be more efficient when | ||
| 1957 | done by hand instead of with packed struct</p> | ||
| 1958 | {#header_close#} | ||
| 1950 | {#header_open|struct Naming#} | 1959 | {#header_open|struct Naming#} |
| 1951 | <p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p> | 1960 | <p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p> |
| 1952 | <ul> | 1961 | <ul> |
| ... | @@ -5135,6 +5144,18 @@ fn seq(c: u8) void { | ... | @@ -5135,6 +5144,18 @@ fn seq(c: u8) void { |
| 5135 | Works at compile-time if {#syntax#}value{#endsyntax#} 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. | 5144 | Works at compile-time if {#syntax#}value{#endsyntax#} 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. |
| 5136 | </p> | 5145 | </p> |
| 5137 | {#header_close#} | 5146 | {#header_close#} |
| 5147 | {#header_open|@bitOffsetOf#} | ||
| 5148 | <pre>{#syntax#}@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}</pre> | ||
| 5149 | <p> | ||
| 5150 | Returns the bit offset of a field relative to its containing struct. | ||
| 5151 | </p> | ||
| 5152 | <p> | ||
| 5153 | For non {#link|packed structs|packed struct#}, this will always be divisible by {#syntax#}8{#endsyntax#}. | ||
| 5154 | For packed structs, non-byte-aligned fields will share a byte offset, but they will have different | ||
| 5155 | bit offsets. | ||
| 5156 | </p> | ||
| 5157 | {#see_also|@byteOffsetOf#} | ||
| 5158 | {#header_close#} | ||
| 5138 | {#header_open|@breakpoint#} | 5159 | {#header_open|@breakpoint#} |
| 5139 | <pre>{#syntax#}@breakpoint(){#endsyntax#}</pre> | 5160 | <pre>{#syntax#}@breakpoint(){#endsyntax#}</pre> |
| 5140 | <p> | 5161 | <p> |
| ... | @@ -5145,6 +5166,13 @@ fn seq(c: u8) void { | ... | @@ -5145,6 +5166,13 @@ fn seq(c: u8) void { |
| 5145 | This function is only valid within function scope. | 5166 | This function is only valid within function scope. |
| 5146 | </p> | 5167 | </p> |
| 5147 | 5168 | ||
| 5169 | {#header_close#} | ||
| 5170 | {#header_open|@byteOffsetOf#} | ||
| 5171 | <pre>{#syntax#}@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}</pre> | ||
| 5172 | <p> | ||
| 5173 | Returns the byte offset of a field relative to its containing struct. | ||
| 5174 | </p> | ||
| 5175 | {#see_also|@bitOffsetOf#} | ||
| 5148 | {#header_close#} | 5176 | {#header_close#} |
| 5149 | {#header_open|@alignCast#} | 5177 | {#header_open|@alignCast#} |
| 5150 | <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}</pre> | 5178 | <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}</pre> |
| ... | @@ -5868,18 +5896,6 @@ fn add(a: i32, b: i32) i32 { | ... | @@ -5868,18 +5896,6 @@ fn add(a: i32, b: i32) i32 { |
| 5868 | </p> | 5896 | </p> |
| 5869 | {#see_also|@inlineCall#} | 5897 | {#see_also|@inlineCall#} |
| 5870 | {#header_close#} | 5898 | {#header_close#} |
| 5871 | {#header_open|@offsetOf#} | ||
| 5872 | <pre>{#syntax#}@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}</pre> | ||
| 5873 | <p> | ||
| 5874 | This function returns the byte offset of a field relative to its containing struct. | ||
| 5875 | </p> | ||
| 5876 | {#header_close#} | ||
| 5877 | {#header_open|@bitOffsetOf#} | ||
| 5878 | <pre><code class="zig">@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)</code></pre> | ||
| 5879 | <p> | ||
| 5880 | This function returns the bit offset of a field relative to its containing struct. | ||
| 5881 | </p> | ||
| 5882 | {#header_close#} | ||
| 5883 | {#header_open|@OpaqueType#} | 5899 | {#header_open|@OpaqueType#} |
| 5884 | <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> | 5900 | <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> |
| 5885 | <p> | 5901 | <p> |
src/ir.cpp+19-13| ... | @@ -17088,30 +17088,36 @@ static ZigType *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, | ... | @@ -17088,30 +17088,36 @@ static ZigType *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, |
| 17088 | IrInstructionByteOffsetOf *instruction) | 17088 | IrInstructionByteOffsetOf *instruction) |
| 17089 | { | 17089 | { |
| 17090 | IrInstruction *type_value = instruction->type_value->other; | 17090 | IrInstruction *type_value = instruction->type_value->other; |
| 17091 | if (type_is_invalid(type_value->value.type)) | ||
| 17092 | return ira->codegen->builtin_types.entry_invalid; | ||
| 17093 | |||
| 17091 | IrInstruction *field_name_value = instruction->field_name->other; | 17094 | IrInstruction *field_name_value = instruction->field_name->other; |
| 17092 | size_t byte_offset = 0; | 17095 | size_t byte_offset = 0; |
| 17093 | if (validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) { | 17096 | if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) |
| 17094 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | 17097 | return ira->codegen->builtin_types.entry_invalid; |
| 17095 | bigint_init_unsigned(&out_val->data.x_bigint, byte_offset); | 17098 | |
| 17096 | return ira->codegen->builtin_types.entry_num_lit_int; | 17099 | |
| 17097 | } | 17100 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 17098 | return ira->codegen->builtin_types.entry_invalid; | 17101 | bigint_init_unsigned(&out_val->data.x_bigint, byte_offset); |
| 17102 | return ira->codegen->builtin_types.entry_num_lit_int; | ||
| 17099 | } | 17103 | } |
| 17100 | 17104 | ||
| 17101 | static ZigType *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, | 17105 | static ZigType *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, |
| 17102 | IrInstructionBitOffsetOf *instruction) | 17106 | IrInstructionBitOffsetOf *instruction) |
| 17103 | { | 17107 | { |
| 17104 | IrInstruction *type_value = instruction->type_value->other; | 17108 | IrInstruction *type_value = instruction->type_value->other; |
| 17109 | if (type_is_invalid(type_value->value.type)) | ||
| 17110 | return ira->codegen->builtin_types.entry_invalid; | ||
| 17105 | IrInstruction *field_name_value = instruction->field_name->other; | 17111 | IrInstruction *field_name_value = instruction->field_name->other; |
| 17106 | size_t byte_offset = 0; | 17112 | size_t byte_offset = 0; |
| 17107 | TypeStructField *field = nullptr; | 17113 | TypeStructField *field = nullptr; |
| 17108 | if ((field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) { | 17114 | if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) |
| 17109 | size_t bit_offset = byte_offset * 8 + field->packed_bits_offset; | 17115 | return ira->codegen->builtin_types.entry_invalid; |
| 17110 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | 17116 | |
| 17111 | bigint_init_unsigned(&out_val->data.x_bigint, bit_offset); | 17117 | size_t bit_offset = byte_offset * 8 + field->packed_bits_offset; |
| 17112 | return ira->codegen->builtin_types.entry_num_lit_int; | 17118 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 17113 | } | 17119 | bigint_init_unsigned(&out_val->data.x_bigint, bit_offset); |
| 17114 | return ira->codegen->builtin_types.entry_invalid; | 17120 | return ira->codegen->builtin_types.entry_num_lit_int; |
| 17115 | } | 17121 | } |
| 17116 | 17122 | ||
| 17117 | static void ensure_field_index(ZigType *type, const char *field_name, size_t index) | 17123 | static void ensure_field_index(ZigType *type, const char *field_name, size_t index) |
std/c/darwin.zig+13-13| ... | @@ -158,12 +158,12 @@ const std = @import("../index.zig"); | ... | @@ -158,12 +158,12 @@ const std = @import("../index.zig"); |
| 158 | const assert = std.debug.assert; | 158 | const assert = std.debug.assert; |
| 159 | 159 | ||
| 160 | comptime { | 160 | comptime { |
| 161 | assert(@offsetOf(Kevent, "ident") == 0); | 161 | assert(@byteOffsetOf(Kevent, "ident") == 0); |
| 162 | assert(@offsetOf(Kevent, "filter") == 8); | 162 | assert(@byteOffsetOf(Kevent, "filter") == 8); |
| 163 | assert(@offsetOf(Kevent, "flags") == 10); | 163 | assert(@byteOffsetOf(Kevent, "flags") == 10); |
| 164 | assert(@offsetOf(Kevent, "fflags") == 12); | 164 | assert(@byteOffsetOf(Kevent, "fflags") == 12); |
| 165 | assert(@offsetOf(Kevent, "data") == 16); | 165 | assert(@byteOffsetOf(Kevent, "data") == 16); |
| 166 | assert(@offsetOf(Kevent, "udata") == 24); | 166 | assert(@byteOffsetOf(Kevent, "udata") == 24); |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | pub const kevent64_s = extern struct { | 169 | pub const kevent64_s = extern struct { |
| ... | @@ -180,11 +180,11 @@ pub const kevent64_s = extern struct { | ... | @@ -180,11 +180,11 @@ pub const kevent64_s = extern struct { |
| 180 | // to make sure the struct is laid out the same. These values were | 180 | // to make sure the struct is laid out the same. These values were |
| 181 | // produced from C code using the offsetof macro. | 181 | // produced from C code using the offsetof macro. |
| 182 | comptime { | 182 | comptime { |
| 183 | assert(@offsetOf(kevent64_s, "ident") == 0); | 183 | assert(@byteOffsetOf(kevent64_s, "ident") == 0); |
| 184 | assert(@offsetOf(kevent64_s, "filter") == 8); | 184 | assert(@byteOffsetOf(kevent64_s, "filter") == 8); |
| 185 | assert(@offsetOf(kevent64_s, "flags") == 10); | 185 | assert(@byteOffsetOf(kevent64_s, "flags") == 10); |
| 186 | assert(@offsetOf(kevent64_s, "fflags") == 12); | 186 | assert(@byteOffsetOf(kevent64_s, "fflags") == 12); |
| 187 | assert(@offsetOf(kevent64_s, "data") == 16); | 187 | assert(@byteOffsetOf(kevent64_s, "data") == 16); |
| 188 | assert(@offsetOf(kevent64_s, "udata") == 24); | 188 | assert(@byteOffsetOf(kevent64_s, "udata") == 24); |
| 189 | assert(@offsetOf(kevent64_s, "ext") == 32); | 189 | assert(@byteOffsetOf(kevent64_s, "ext") == 32); |
| 190 | } | 190 | } |
test/cases/sizeof_and_typeof.zig+40-21| ... | @@ -1,13 +1,14 @@ | ... | @@ -1,13 +1,14 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const assert = @import("std").debug.assert; | 2 | const assert = @import("std").debug.assert; |
| 2 | 3 | ||
| 3 | test "sizeofAndTypeOf" { | 4 | test "@sizeOf and @typeOf" { |
| 4 | const y: @typeOf(x) = 120; | 5 | const y: @typeOf(x) = 120; |
| 5 | assert(@sizeOf(@typeOf(y)) == 2); | 6 | assert(@sizeOf(@typeOf(y)) == 2); |
| 6 | } | 7 | } |
| 7 | const x: u16 = 13; | 8 | const x: u16 = 13; |
| 8 | const z: @typeOf(x) = 19; | 9 | const z: @typeOf(x) = 19; |
| 9 | 10 | ||
| 10 | const S = struct { | 11 | const A = struct { |
| 11 | a: u8, | 12 | a: u8, |
| 12 | b: u32, | 13 | b: u32, |
| 13 | c: u8, | 14 | c: u8, |
| ... | @@ -27,24 +28,42 @@ const P = packed struct { | ... | @@ -27,24 +28,42 @@ const P = packed struct { |
| 27 | g: u16, | 28 | g: u16, |
| 28 | }; | 29 | }; |
| 29 | 30 | ||
| 30 | test "byteOffsetOf" { | 31 | test "@byteOffsetOf" { |
| 31 | const p: P = undefined; | 32 | // Packed structs have fixed memory layout |
| 32 | std.debug.assert(@byteOffsetOf(P, "a") == 0 and @byteOffsetOf(S, "a") == 0); | 33 | assert(@byteOffsetOf(P, "a") == 0); |
| 33 | std.debug.assert(@byteOffsetOf(P, "b") == 1 and @byteOffsetOf(S, "b") == 4); | 34 | assert(@byteOffsetOf(P, "b") == 1); |
| 34 | std.debug.assert(@byteOffsetOf(P, "c") == 5 and @byteOffsetOf(S, "c") == 8); | 35 | assert(@byteOffsetOf(P, "c") == 5); |
| 35 | std.debug.assert(@byteOffsetOf(P, "d") == 6 and @byteOffsetOf(S, "d") == 9); | 36 | assert(@byteOffsetOf(P, "d") == 6); |
| 36 | std.debug.assert(@byteOffsetOf(P, "e") == 6 and @byteOffsetOf(S, "e") == 10); | 37 | assert(@byteOffsetOf(P, "e") == 6); |
| 37 | std.debug.assert(@byteOffsetOf(P, "f") == 7 and @byteOffsetOf(S, "f") == 12); | 38 | assert(@byteOffsetOf(P, "f") == 7); |
| 38 | std.debug.assert(@byteOffsetOf(P, "g") == 9 and @byteOffsetOf(S, "g") == 14); | 39 | assert(@byteOffsetOf(P, "g") == 9); |
| 40 | |||
| 41 | // Normal struct fields can be moved/padded | ||
| 42 | var a: A = undefined; | ||
| 43 | assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); | ||
| 44 | assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); | ||
| 45 | assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); | ||
| 46 | assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); | ||
| 47 | assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); | ||
| 48 | assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); | ||
| 49 | assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); | ||
| 39 | } | 50 | } |
| 40 | 51 | ||
| 41 | test "bitOffsetOf" { | ||
| 42 | const p: P = undefined; | ||
| 43 | std.debug.assert(@bitOffsetOf(P, "a") == 0 and @bitOffsetOf(S, "a") == 0); | ||
| 44 | std.debug.assert(@bitOffsetOf(P, "b") == 8 and @bitOffsetOf(S, "b") == 32); | ||
| 45 | std.debug.assert(@bitOffsetOf(P, "c") == 40 and @bitOffsetOf(S, "c") == 64); | ||
| 46 | std.debug.assert(@bitOffsetOf(P, "d") == 48 and @bitOffsetOf(S, "d") == 72); | ||
| 47 | std.debug.assert(@bitOffsetOf(P, "e") == 51 and @bitOffsetOf(S, "e") == 80); | ||
| 48 | std.debug.assert(@bitOffsetOf(P, "f") == 56 and @bitOffsetOf(S, "f") == 96); | ||
| 49 | std.debug.assert(@bitOffsetOf(P, "g") == 72 and @bitOffsetOf(S, "g") == 112); | ||
| 50 | } | ||
| \ No newline at end of file | |||
| 52 | test "@bitOffsetOf" { | ||
| 53 | // Packed structs have fixed memory layout | ||
| 54 | assert(@bitOffsetOf(P, "a") == 0); | ||
| 55 | assert(@bitOffsetOf(P, "b") == 8); | ||
| 56 | assert(@bitOffsetOf(P, "c") == 40); | ||
| 57 | assert(@bitOffsetOf(P, "d") == 48); | ||
| 58 | assert(@bitOffsetOf(P, "e") == 51); | ||
| 59 | assert(@bitOffsetOf(P, "f") == 56); | ||
| 60 | assert(@bitOffsetOf(P, "g") == 72); | ||
| 61 | |||
| 62 | assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); | ||
| 63 | assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); | ||
| 64 | assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); | ||
| 65 | assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); | ||
| 66 | assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); | ||
| 67 | assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); | ||
| 68 | assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); | ||
| 69 | } | ||
test/compile_errors.zig+4-4| ... | @@ -3769,7 +3769,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -3769,7 +3769,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3769 | \\ return @byteOffsetOf(Foo, "a",); | 3769 | \\ return @byteOffsetOf(Foo, "a",); |
| 3770 | \\} | 3770 | \\} |
| 3771 | , | 3771 | , |
| 3772 | ".tmp_source.zig:3:22: error: expected struct type, found 'i32'", | 3772 | ".tmp_source.zig:3:26: error: expected struct type, found 'i32'", |
| 3773 | ); | 3773 | ); |
| 3774 | 3774 | ||
| 3775 | cases.add( | 3775 | cases.add( |
| ... | @@ -3781,7 +3781,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -3781,7 +3781,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3781 | \\ return @byteOffsetOf(Foo, "a",); | 3781 | \\ return @byteOffsetOf(Foo, "a",); |
| 3782 | \\} | 3782 | \\} |
| 3783 | , | 3783 | , |
| 3784 | ".tmp_source.zig:5:27: error: struct 'Foo' has no field 'a'", | 3784 | ".tmp_source.zig:5:31: error: struct 'Foo' has no field 'a'", |
| 3785 | ); | 3785 | ); |
| 3786 | 3786 | ||
| 3787 | cases.addExe( | 3787 | cases.addExe( |
| ... | @@ -5092,7 +5092,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -5092,7 +5092,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5092 | \\ const fieldOffset = @byteOffsetOf(Empty, "val",); | 5092 | \\ const fieldOffset = @byteOffsetOf(Empty, "val",); |
| 5093 | \\} | 5093 | \\} |
| 5094 | , | 5094 | , |
| 5095 | ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset", | 5095 | ".tmp_source.zig:5:46: error: zero-bit field 'val' in struct 'Empty' has no offset", |
| 5096 | ); | 5096 | ); |
| 5097 | 5097 | ||
| 5098 | cases.add( | 5098 | cases.add( |
| ... | @@ -5104,7 +5104,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -5104,7 +5104,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5104 | \\ const fieldOffset = @bitOffsetOf(Empty, "val",); | 5104 | \\ const fieldOffset = @bitOffsetOf(Empty, "val",); |
| 5105 | \\} | 5105 | \\} |
| 5106 | , | 5106 | , |
| 5107 | ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset", | 5107 | ".tmp_source.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset", |
| 5108 | ); | 5108 | ); |
| 5109 | 5109 | ||
| 5110 | cases.add( | 5110 | cases.add( |