| author | |
| committer | |
| log | 736d14fd5fa5feea83a6efce8b606b62bf165033 |
| tree | da48c5f1743c7418ed51c5f9769236529a988be8 |
| parent | 0ec01e58b429e96d50411f74a0005076c24dda0c |
* AstGen: fix not emitting `struct_init_empty` when an explicit type is
present in struct initialization syntax.
* AstGen: these two syntaxes now lower to identical ZIR:
- `var a = A{ .b = c };`
- `var a = @as(A, .{ .b = c });`
* Zir: clarify `auto_enum_tag` in the doc comments.
* LLVM Backend: fix lowering of function return types when the type has
0 bits.6 files changed, 1027 insertions(+), 961 deletions(-)
src/AstGen.zig+79-31| ... | ... | @@ -1357,7 +1357,14 @@ fn structInitExpr( |
| 1357 | 1357 | const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) { |
| 1358 | 1358 | .array_type => tree.arrayType(struct_init.ast.type_expr), |
| 1359 | 1359 | .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr), |
| 1360 | else => break :array, | |
| 1360 | else => { | |
| 1361 | if (struct_init.ast.fields.len == 0) { | |
| 1362 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1363 | const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); | |
| 1364 | return rvalue(gz, rl, result, node); | |
| 1365 | } | |
| 1366 | break :array; | |
| 1367 | }, | |
| 1361 | 1368 | }; |
| 1362 | 1369 | const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and |
| 1363 | 1370 | // This intentionally does not support `@"_"` syntax. |
| ... | ... | @@ -1419,8 +1426,8 @@ fn structInitExpr( |
| 1419 | 1426 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); |
| 1420 | 1427 | return rvalue(gz, rl, result, node); |
| 1421 | 1428 | }, |
| 1422 | .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst), | |
| 1423 | .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr), | |
| 1429 | .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst), | |
| 1430 | .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr), | |
| 1424 | 1431 | } |
| 1425 | 1432 | } |
| 1426 | 1433 | |
| ... | ... | @@ -1459,6 +1466,26 @@ fn structInitExprRlNone( |
| 1459 | 1466 | } |
| 1460 | 1467 | |
| 1461 | 1468 | fn structInitExprRlPtr( |
| 1469 | gz: *GenZir, | |
| 1470 | scope: *Scope, | |
| 1471 | rl: ResultLoc, | |
| 1472 | node: Ast.Node.Index, | |
| 1473 | struct_init: Ast.full.StructInit, | |
| 1474 | result_ptr: Zir.Inst.Ref, | |
| 1475 | ) InnerError!Zir.Inst.Ref { | |
| 1476 | if (struct_init.ast.type_expr == 0) { | |
| 1477 | return structInitExprRlPtrInner(gz, scope, node, struct_init, result_ptr); | |
| 1478 | } | |
| 1479 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1480 | ||
| 1481 | var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr); | |
| 1482 | defer as_scope.instructions.deinit(gz.astgen.gpa); | |
| 1483 | ||
| 1484 | const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr); | |
| 1485 | return as_scope.finishCoercion(gz, rl, node, result, ty_inst); | |
| 1486 | } | |
| 1487 | ||
| 1488 | fn structInitExprRlPtrInner( | |
| 1462 | 1489 | gz: *GenZir, |
| 1463 | 1490 | scope: *Scope, |
| 1464 | 1491 | node: Ast.Node.Index, |
| ... | ... | @@ -1472,9 +1499,6 @@ fn structInitExprRlPtr( |
| 1472 | 1499 | const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); |
| 1473 | 1500 | defer gpa.free(field_ptr_list); |
| 1474 | 1501 | |
| 1475 | if (struct_init.ast.type_expr != 0) | |
| 1476 | _ = try typeExpr(gz, scope, struct_init.ast.type_expr); | |
| 1477 | ||
| 1478 | 1502 | for (struct_init.ast.fields) |field_init, i| { |
| 1479 | 1503 | const name_token = tree.firstToken(field_init) - 2; |
| 1480 | 1504 | const str_index = try astgen.identAsString(name_token); |
| ... | ... | @@ -1489,7 +1513,7 @@ fn structInitExprRlPtr( |
| 1489 | 1513 | .body_len = @intCast(u32, field_ptr_list.len), |
| 1490 | 1514 | }); |
| 1491 | 1515 | try astgen.extra.appendSlice(gpa, field_ptr_list); |
| 1492 | return .void_value; | |
| 1516 | return Zir.Inst.Ref.void_value; | |
| 1493 | 1517 | } |
| 1494 | 1518 | |
| 1495 | 1519 | fn structInitExprRlTy( |
| ... | ... | @@ -6902,35 +6926,13 @@ fn asRlPtr( |
| 6902 | 6926 | operand_node: Ast.Node.Index, |
| 6903 | 6927 | dest_type: Zir.Inst.Ref, |
| 6904 | 6928 | ) InnerError!Zir.Inst.Ref { |
| 6905 | // Detect whether this expr() call goes into rvalue() to store the result into the | |
| 6906 | // result location. If it does, elide the coerce_result_ptr instruction | |
| 6907 | // as well as the store instruction, instead passing the result as an rvalue. | |
| 6908 | 6929 | const astgen = parent_gz.astgen; |
| 6909 | 6930 | |
| 6910 | var as_scope = parent_gz.makeSubBlock(scope); | |
| 6931 | var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr); | |
| 6911 | 6932 | defer as_scope.instructions.deinit(astgen.gpa); |
| 6912 | 6933 | |
| 6913 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | |
| 6914 | 6934 | const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node); |
| 6915 | const parent_zir = &parent_gz.instructions; | |
| 6916 | if (as_scope.rvalue_rl_count == 1) { | |
| 6917 | // Busted! This expression didn't actually need a pointer. | |
| 6918 | const zir_tags = astgen.instructions.items(.tag); | |
| 6919 | const zir_datas = astgen.instructions.items(.data); | |
| 6920 | try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); | |
| 6921 | for (as_scope.instructions.items) |src_inst| { | |
| 6922 | if (indexToRef(src_inst) == as_scope.rl_ptr) continue; | |
| 6923 | if (zir_tags[src_inst] == .store_to_block_ptr) { | |
| 6924 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | |
| 6925 | } | |
| 6926 | parent_zir.appendAssumeCapacity(src_inst); | |
| 6927 | } | |
| 6928 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | |
| 6929 | return rvalue(parent_gz, rl, casted_result, operand_node); | |
| 6930 | } else { | |
| 6931 | try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items); | |
| 6932 | return result; | |
| 6933 | } | |
| 6935 | return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type); | |
| 6934 | 6936 | } |
| 6935 | 6937 | |
| 6936 | 6938 | fn bitCast( |
| ... | ... | @@ -9108,6 +9110,52 @@ const GenZir = struct { |
| 9108 | 9110 | }; |
| 9109 | 9111 | } |
| 9110 | 9112 | |
| 9113 | fn makeCoercionScope( | |
| 9114 | parent_gz: *GenZir, | |
| 9115 | scope: *Scope, | |
| 9116 | dest_type: Zir.Inst.Ref, | |
| 9117 | result_ptr: Zir.Inst.Ref, | |
| 9118 | ) !GenZir { | |
| 9119 | // Detect whether this expr() call goes into rvalue() to store the result into the | |
| 9120 | // result location. If it does, elide the coerce_result_ptr instruction | |
| 9121 | // as well as the store instruction, instead passing the result as an rvalue. | |
| 9122 | var as_scope = parent_gz.makeSubBlock(scope); | |
| 9123 | errdefer as_scope.instructions.deinit(parent_gz.astgen.gpa); | |
| 9124 | as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); | |
| 9125 | ||
| 9126 | return as_scope; | |
| 9127 | } | |
| 9128 | ||
| 9129 | fn finishCoercion( | |
| 9130 | as_scope: *GenZir, | |
| 9131 | parent_gz: *GenZir, | |
| 9132 | rl: ResultLoc, | |
| 9133 | src_node: Ast.Node.Index, | |
| 9134 | result: Zir.Inst.Ref, | |
| 9135 | dest_type: Zir.Inst.Ref, | |
| 9136 | ) !Zir.Inst.Ref { | |
| 9137 | const astgen = as_scope.astgen; | |
| 9138 | const parent_zir = &parent_gz.instructions; | |
| 9139 | if (as_scope.rvalue_rl_count == 1) { | |
| 9140 | // Busted! This expression didn't actually need a pointer. | |
| 9141 | const zir_tags = astgen.instructions.items(.tag); | |
| 9142 | const zir_datas = astgen.instructions.items(.data); | |
| 9143 | try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); | |
| 9144 | for (as_scope.instructions.items) |src_inst| { | |
| 9145 | if (indexToRef(src_inst) == as_scope.rl_ptr) continue; | |
| 9146 | if (zir_tags[src_inst] == .store_to_block_ptr) { | |
| 9147 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | |
| 9148 | } | |
| 9149 | parent_zir.appendAssumeCapacity(src_inst); | |
| 9150 | } | |
| 9151 | const casted_result = try parent_gz.addBin(.as, dest_type, result); | |
| 9152 | return rvalue(parent_gz, rl, casted_result, src_node); | |
| 9153 | } else { | |
| 9154 | try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items); | |
| 9155 | return result; | |
| 9156 | } | |
| 9157 | } | |
| 9158 | ||
| 9111 | 9159 | const Label = struct { |
| 9112 | 9160 | token: Ast.TokenIndex, |
| 9113 | 9161 | block_inst: Zir.Inst.Index, |
src/Zir.zig+6-2| ... | ... | @@ -2650,8 +2650,12 @@ pub const Inst = struct { |
| 2650 | 2650 | has_decls_len: bool, |
| 2651 | 2651 | name_strategy: NameStrategy, |
| 2652 | 2652 | layout: std.builtin.TypeInfo.ContainerLayout, |
| 2653 | /// false: union(tag_type) | |
| 2654 | /// true: union(enum(tag_type)) | |
| 2653 | /// has_tag_type | auto_enum_tag | result | |
| 2654 | /// ------------------------------------- | |
| 2655 | /// false | false | union { } | |
| 2656 | /// false | true | union(enum) { } | |
| 2657 | /// true | true | union(enum(T)) { } | |
| 2658 | /// true | false | union(T) { } | |
| 2655 | 2659 | auto_enum_tag: bool, |
| 2656 | 2660 | _: u6 = undefined, |
| 2657 | 2661 | }; |
src/codegen/llvm.zig+6-1| ... | ... | @@ -563,8 +563,13 @@ pub const DeclGen = struct { |
| 563 | 563 | } |
| 564 | 564 | } |
| 565 | 565 | |
| 566 | const llvm_ret_ty = if (!return_type.hasCodeGenBits()) | |
| 567 | self.context.voidType() | |
| 568 | else | |
| 569 | try self.llvmType(return_type); | |
| 570 | ||
| 566 | 571 | const fn_type = llvm.functionType( |
| 567 | try self.llvmType(return_type), | |
| 572 | llvm_ret_ty, | |
| 568 | 573 | llvm_param_buffer.ptr, |
| 569 | 574 | llvm_params_len, |
| 570 | 575 | .False, |
test/behavior.zig+2-1| ... | ... | @@ -13,6 +13,7 @@ test { |
| 13 | 13 | _ = @import("behavior/atomics.zig"); |
| 14 | 14 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| 15 | 15 | _ = @import("behavior/translate_c_macros.zig"); |
| 16 | _ = @import("behavior/struct.zig"); | |
| 16 | 17 | _ = @import("behavior/union.zig"); |
| 17 | 18 | _ = @import("behavior/widening.zig"); |
| 18 | 19 | |
| ... | ... | @@ -135,7 +136,7 @@ test { |
| 135 | 136 | _ = @import("behavior/sizeof_and_typeof_stage1.zig"); |
| 136 | 137 | _ = @import("behavior/slice.zig"); |
| 137 | 138 | _ = @import("behavior/slice_sentinel_comptime.zig"); |
| 138 | _ = @import("behavior/struct.zig"); | |
| 139 | _ = @import("behavior/struct_stage1.zig"); | |
| 139 | 140 | _ = @import("behavior/struct_contains_null_ptr_itself.zig"); |
| 140 | 141 | _ = @import("behavior/struct_contains_slice_of_itself.zig"); |
| 141 | 142 | _ = @import("behavior/switch.zig"); |
test/behavior/struct.zig+6-926| ... | ... | @@ -5,949 +5,29 @@ const expect = std.testing.expect; |
| 5 | 5 | const expectEqual = std.testing.expectEqual; |
| 6 | 6 | const expectEqualSlices = std.testing.expectEqualSlices; |
| 7 | 7 | const maxInt = std.math.maxInt; |
| 8 | ||
| 8 | 9 | const StructWithNoFields = struct { |
| 9 | 10 | fn add(a: i32, b: i32) i32 { |
| 10 | 11 | return a + b; |
| 11 | 12 | } |
| 12 | 13 | }; |
| 13 | const empty_global_instance = StructWithNoFields{}; | |
| 14 | ||
| 15 | top_level_field: i32, | |
| 16 | ||
| 17 | test "top level fields" { | |
| 18 | var instance = @This(){ | |
| 19 | .top_level_field = 1234, | |
| 20 | }; | |
| 21 | instance.top_level_field += 1; | |
| 22 | try expectEqual(@as(i32, 1235), instance.top_level_field); | |
| 23 | } | |
| 24 | 14 | |
| 25 | 15 | test "call struct static method" { |
| 26 | 16 | const result = StructWithNoFields.add(3, 4); |
| 27 | 17 | try expect(result == 7); |
| 28 | 18 | } |
| 29 | 19 | |
| 30 | test "return empty struct instance" { | |
| 31 | _ = returnEmptyStructInstance(); | |
| 32 | } | |
| 33 | fn returnEmptyStructInstance() StructWithNoFields { | |
| 34 | return empty_global_instance; | |
| 35 | } | |
| 36 | ||
| 37 | 20 | const should_be_11 = StructWithNoFields.add(5, 6); |
| 38 | 21 | |
| 39 | 22 | test "invoke static method in global scope" { |
| 40 | 23 | try expect(should_be_11 == 11); |
| 41 | 24 | } |
| 42 | 25 | |
| 43 | test "void struct fields" { | |
| 44 | const foo = VoidStructFieldsFoo{ | |
| 45 | .a = void{}, | |
| 46 | .b = 1, | |
| 47 | .c = void{}, | |
| 48 | }; | |
| 49 | try expect(foo.b == 1); | |
| 50 | try expect(@sizeOf(VoidStructFieldsFoo) == 4); | |
| 51 | } | |
| 52 | const VoidStructFieldsFoo = struct { | |
| 53 | a: void, | |
| 54 | b: i32, | |
| 55 | c: void, | |
| 56 | }; | |
| 57 | ||
| 58 | test "structs" { | |
| 59 | var foo: StructFoo = undefined; | |
| 60 | @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); | |
| 61 | foo.a += 1; | |
| 62 | foo.b = foo.a == 1; | |
| 63 | try testFoo(foo); | |
| 64 | testMutation(&foo); | |
| 65 | try expect(foo.c == 100); | |
| 66 | } | |
| 67 | const StructFoo = struct { | |
| 68 | a: i32, | |
| 69 | b: bool, | |
| 70 | c: f32, | |
| 71 | }; | |
| 72 | fn testFoo(foo: StructFoo) !void { | |
| 73 | try expect(foo.b); | |
| 74 | } | |
| 75 | fn testMutation(foo: *StructFoo) void { | |
| 76 | foo.c = 100; | |
| 77 | } | |
| 78 | ||
| 79 | const Node = struct { | |
| 80 | val: Val, | |
| 81 | next: *Node, | |
| 82 | }; | |
| 83 | ||
| 84 | const Val = struct { | |
| 85 | x: i32, | |
| 86 | }; | |
| 87 | ||
| 88 | test "struct point to self" { | |
| 89 | var root: Node = undefined; | |
| 90 | root.val.x = 1; | |
| 91 | ||
| 92 | var node: Node = undefined; | |
| 93 | node.next = &root; | |
| 94 | node.val.x = 2; | |
| 95 | ||
| 96 | root.next = &node; | |
| 97 | ||
| 98 | try expect(node.next.next.next.val.x == 1); | |
| 99 | } | |
| 100 | ||
| 101 | test "struct byval assign" { | |
| 102 | var foo1: StructFoo = undefined; | |
| 103 | var foo2: StructFoo = undefined; | |
| 104 | ||
| 105 | foo1.a = 1234; | |
| 106 | foo2.a = 0; | |
| 107 | try expect(foo2.a == 0); | |
| 108 | foo2 = foo1; | |
| 109 | try expect(foo2.a == 1234); | |
| 110 | } | |
| 111 | ||
| 112 | fn structInitializer() void { | |
| 113 | const val = Val{ .x = 42 }; | |
| 114 | try expect(val.x == 42); | |
| 115 | } | |
| 116 | ||
| 117 | test "fn call of struct field" { | |
| 118 | const Foo = struct { | |
| 119 | ptr: fn () i32, | |
| 120 | }; | |
| 121 | const S = struct { | |
| 122 | fn aFunc() i32 { | |
| 123 | return 13; | |
| 124 | } | |
| 125 | ||
| 126 | fn callStructField(foo: Foo) i32 { | |
| 127 | return foo.ptr(); | |
| 128 | } | |
| 129 | }; | |
| 130 | ||
| 131 | try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); | |
| 132 | } | |
| 133 | ||
| 134 | test "store member function in variable" { | |
| 135 | const instance = MemberFnTestFoo{ .x = 1234 }; | |
| 136 | const memberFn = MemberFnTestFoo.member; | |
| 137 | const result = memberFn(instance); | |
| 138 | try expect(result == 1234); | |
| 139 | } | |
| 140 | const MemberFnTestFoo = struct { | |
| 141 | x: i32, | |
| 142 | fn member(foo: MemberFnTestFoo) i32 { | |
| 143 | return foo.x; | |
| 144 | } | |
| 145 | }; | |
| 146 | ||
| 147 | test "call member function directly" { | |
| 148 | const instance = MemberFnTestFoo{ .x = 1234 }; | |
| 149 | const result = MemberFnTestFoo.member(instance); | |
| 150 | try expect(result == 1234); | |
| 151 | } | |
| 152 | ||
| 153 | test "member functions" { | |
| 154 | const r = MemberFnRand{ .seed = 1234 }; | |
| 155 | try expect(r.getSeed() == 1234); | |
| 156 | } | |
| 157 | const MemberFnRand = struct { | |
| 158 | seed: u32, | |
| 159 | pub fn getSeed(r: *const MemberFnRand) u32 { | |
| 160 | return r.seed; | |
| 161 | } | |
| 162 | }; | |
| 163 | ||
| 164 | test "return struct byval from function" { | |
| 165 | const bar = makeBar2(1234, 5678); | |
| 166 | try expect(bar.y == 5678); | |
| 167 | } | |
| 168 | const Bar = struct { | |
| 169 | x: i32, | |
| 170 | y: i32, | |
| 171 | }; | |
| 172 | fn makeBar2(x: i32, y: i32) Bar { | |
| 173 | return Bar{ | |
| 174 | .x = x, | |
| 175 | .y = y, | |
| 176 | }; | |
| 177 | } | |
| 178 | ||
| 179 | test "empty struct method call" { | |
| 180 | const es = EmptyStruct{}; | |
| 181 | try expect(es.method() == 1234); | |
| 182 | } | |
| 183 | const EmptyStruct = struct { | |
| 184 | fn method(es: *const EmptyStruct) i32 { | |
| 185 | _ = es; | |
| 186 | return 1234; | |
| 187 | } | |
| 188 | }; | |
| 189 | ||
| 190 | test "return empty struct from fn" { | |
| 191 | _ = testReturnEmptyStructFromFn(); | |
| 192 | } | |
| 193 | const EmptyStruct2 = struct {}; | |
| 194 | fn testReturnEmptyStructFromFn() EmptyStruct2 { | |
| 195 | return EmptyStruct2{}; | |
| 196 | } | |
| 197 | ||
| 198 | test "pass slice of empty struct to fn" { | |
| 199 | try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); | |
| 200 | } | |
| 201 | fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { | |
| 202 | return slice.len; | |
| 203 | } | |
| 204 | ||
| 205 | const APackedStruct = packed struct { | |
| 206 | x: u8, | |
| 207 | y: u8, | |
| 208 | }; | |
| 209 | ||
| 210 | test "packed struct" { | |
| 211 | var foo = APackedStruct{ | |
| 212 | .x = 1, | |
| 213 | .y = 2, | |
| 214 | }; | |
| 215 | foo.y += 1; | |
| 216 | const four = foo.x + foo.y; | |
| 217 | try expect(four == 4); | |
| 218 | } | |
| 219 | ||
| 220 | const BitField1 = packed struct { | |
| 221 | a: u3, | |
| 222 | b: u3, | |
| 223 | c: u2, | |
| 224 | }; | |
| 225 | ||
| 226 | const bit_field_1 = BitField1{ | |
| 227 | .a = 1, | |
| 228 | .b = 2, | |
| 229 | .c = 3, | |
| 230 | }; | |
| 231 | ||
| 232 | test "bit field access" { | |
| 233 | var data = bit_field_1; | |
| 234 | try expect(getA(&data) == 1); | |
| 235 | try expect(getB(&data) == 2); | |
| 236 | try expect(getC(&data) == 3); | |
| 237 | comptime try expect(@sizeOf(BitField1) == 1); | |
| 238 | ||
| 239 | data.b += 1; | |
| 240 | try expect(data.b == 3); | |
| 241 | ||
| 242 | data.a += 1; | |
| 243 | try expect(data.a == 2); | |
| 244 | try expect(data.b == 3); | |
| 245 | } | |
| 246 | ||
| 247 | fn getA(data: *const BitField1) u3 { | |
| 248 | return data.a; | |
| 249 | } | |
| 250 | ||
| 251 | fn getB(data: *const BitField1) u3 { | |
| 252 | return data.b; | |
| 253 | } | |
| 254 | ||
| 255 | fn getC(data: *const BitField1) u2 { | |
| 256 | return data.c; | |
| 257 | } | |
| 258 | ||
| 259 | const Foo24Bits = packed struct { | |
| 260 | field: u24, | |
| 261 | }; | |
| 262 | const Foo96Bits = packed struct { | |
| 263 | a: u24, | |
| 264 | b: u24, | |
| 265 | c: u24, | |
| 266 | d: u24, | |
| 267 | }; | |
| 268 | ||
| 269 | test "packed struct 24bits" { | |
| 270 | comptime { | |
| 271 | try expect(@sizeOf(Foo24Bits) == 4); | |
| 272 | if (@sizeOf(usize) == 4) { | |
| 273 | try expect(@sizeOf(Foo96Bits) == 12); | |
| 274 | } else { | |
| 275 | try expect(@sizeOf(Foo96Bits) == 16); | |
| 276 | } | |
| 277 | } | |
| 278 | ||
| 279 | var value = Foo96Bits{ | |
| 280 | .a = 0, | |
| 281 | .b = 0, | |
| 282 | .c = 0, | |
| 283 | .d = 0, | |
| 284 | }; | |
| 285 | value.a += 1; | |
| 286 | try expect(value.a == 1); | |
| 287 | try expect(value.b == 0); | |
| 288 | try expect(value.c == 0); | |
| 289 | try expect(value.d == 0); | |
| 290 | ||
| 291 | value.b += 1; | |
| 292 | try expect(value.a == 1); | |
| 293 | try expect(value.b == 1); | |
| 294 | try expect(value.c == 0); | |
| 295 | try expect(value.d == 0); | |
| 296 | ||
| 297 | value.c += 1; | |
| 298 | try expect(value.a == 1); | |
| 299 | try expect(value.b == 1); | |
| 300 | try expect(value.c == 1); | |
| 301 | try expect(value.d == 0); | |
| 302 | ||
| 303 | value.d += 1; | |
| 304 | try expect(value.a == 1); | |
| 305 | try expect(value.b == 1); | |
| 306 | try expect(value.c == 1); | |
| 307 | try expect(value.d == 1); | |
| 308 | } | |
| 309 | ||
| 310 | const Foo32Bits = packed struct { | |
| 311 | field: u24, | |
| 312 | pad: u8, | |
| 313 | }; | |
| 314 | ||
| 315 | const FooArray24Bits = packed struct { | |
| 316 | a: u16, | |
| 317 | b: [2]Foo32Bits, | |
| 318 | c: u16, | |
| 319 | }; | |
| 320 | ||
| 321 | // TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 | |
| 322 | test "packed array 24bits" { | |
| 323 | comptime { | |
| 324 | try expect(@sizeOf([9]Foo32Bits) == 9 * 4); | |
| 325 | try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); | |
| 326 | } | |
| 327 | ||
| 328 | var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); | |
| 329 | bytes[bytes.len - 1] = 0xaa; | |
| 330 | const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; | |
| 331 | try expect(ptr.a == 0); | |
| 332 | try expect(ptr.b[0].field == 0); | |
| 333 | try expect(ptr.b[1].field == 0); | |
| 334 | try expect(ptr.c == 0); | |
| 335 | ||
| 336 | ptr.a = maxInt(u16); | |
| 337 | try expect(ptr.a == maxInt(u16)); | |
| 338 | try expect(ptr.b[0].field == 0); | |
| 339 | try expect(ptr.b[1].field == 0); | |
| 340 | try expect(ptr.c == 0); | |
| 341 | ||
| 342 | ptr.b[0].field = maxInt(u24); | |
| 343 | try expect(ptr.a == maxInt(u16)); | |
| 344 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 345 | try expect(ptr.b[1].field == 0); | |
| 346 | try expect(ptr.c == 0); | |
| 347 | ||
| 348 | ptr.b[1].field = maxInt(u24); | |
| 349 | try expect(ptr.a == maxInt(u16)); | |
| 350 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 351 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 352 | try expect(ptr.c == 0); | |
| 353 | ||
| 354 | ptr.c = maxInt(u16); | |
| 355 | try expect(ptr.a == maxInt(u16)); | |
| 356 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 357 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 358 | try expect(ptr.c == maxInt(u16)); | |
| 359 | ||
| 360 | try expect(bytes[bytes.len - 1] == 0xaa); | |
| 361 | } | |
| 362 | ||
| 363 | const FooStructAligned = packed struct { | |
| 364 | a: u8, | |
| 365 | b: u8, | |
| 366 | }; | |
| 367 | ||
| 368 | const FooArrayOfAligned = packed struct { | |
| 369 | a: [2]FooStructAligned, | |
| 370 | }; | |
| 371 | ||
| 372 | test "aligned array of packed struct" { | |
| 373 | comptime { | |
| 374 | try expect(@sizeOf(FooStructAligned) == 2); | |
| 375 | try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); | |
| 376 | } | |
| 377 | ||
| 378 | var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); | |
| 379 | const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; | |
| 380 | ||
| 381 | try expect(ptr.a[0].a == 0xbb); | |
| 382 | try expect(ptr.a[0].b == 0xbb); | |
| 383 | try expect(ptr.a[1].a == 0xbb); | |
| 384 | try expect(ptr.a[1].b == 0xbb); | |
| 385 | } | |
| 386 | ||
| 387 | test "runtime struct initialization of bitfield" { | |
| 388 | const s1 = Nibbles{ | |
| 389 | .x = x1, | |
| 390 | .y = x1, | |
| 391 | }; | |
| 392 | const s2 = Nibbles{ | |
| 393 | .x = @intCast(u4, x2), | |
| 394 | .y = @intCast(u4, x2), | |
| 395 | }; | |
| 396 | ||
| 397 | try expect(s1.x == x1); | |
| 398 | try expect(s1.y == x1); | |
| 399 | try expect(s2.x == @intCast(u4, x2)); | |
| 400 | try expect(s2.y == @intCast(u4, x2)); | |
| 401 | } | |
| 402 | ||
| 403 | var x1 = @as(u4, 1); | |
| 404 | var x2 = @as(u8, 2); | |
| 405 | ||
| 406 | const Nibbles = packed struct { | |
| 407 | x: u4, | |
| 408 | y: u4, | |
| 409 | }; | |
| 410 | ||
| 411 | const Bitfields = packed struct { | |
| 412 | f1: u16, | |
| 413 | f2: u16, | |
| 414 | f3: u8, | |
| 415 | f4: u8, | |
| 416 | f5: u4, | |
| 417 | f6: u4, | |
| 418 | f7: u8, | |
| 419 | }; | |
| 420 | ||
| 421 | test "native bit field understands endianness" { | |
| 422 | var all: u64 = if (native_endian != .Little) | |
| 423 | 0x1111222233445677 | |
| 424 | else | |
| 425 | 0x7765443322221111; | |
| 426 | var bytes: [8]u8 = undefined; | |
| 427 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); | |
| 428 | var bitfields = @ptrCast(*Bitfields, &bytes).*; | |
| 429 | ||
| 430 | try expect(bitfields.f1 == 0x1111); | |
| 431 | try expect(bitfields.f2 == 0x2222); | |
| 432 | try expect(bitfields.f3 == 0x33); | |
| 433 | try expect(bitfields.f4 == 0x44); | |
| 434 | try expect(bitfields.f5 == 0x5); | |
| 435 | try expect(bitfields.f6 == 0x6); | |
| 436 | try expect(bitfields.f7 == 0x77); | |
| 437 | } | |
| 438 | ||
| 439 | test "align 1 field before self referential align 8 field as slice return type" { | |
| 440 | const result = alloc(Expr); | |
| 441 | try expect(result.len == 0); | |
| 442 | } | |
| 443 | ||
| 444 | const Expr = union(enum) { | |
| 445 | Literal: u8, | |
| 446 | Question: *Expr, | |
| 447 | }; | |
| 448 | ||
| 449 | fn alloc(comptime T: type) []T { | |
| 450 | return &[_]T{}; | |
| 451 | } | |
| 452 | ||
| 453 | test "call method with mutable reference to struct with no fields" { | |
| 454 | const S = struct { | |
| 455 | fn doC(s: *const @This()) bool { | |
| 456 | _ = s; | |
| 457 | return true; | |
| 458 | } | |
| 459 | fn do(s: *@This()) bool { | |
| 460 | _ = s; | |
| 461 | return true; | |
| 462 | } | |
| 463 | }; | |
| 464 | ||
| 465 | var s = S{}; | |
| 466 | try expect(S.doC(&s)); | |
| 467 | try expect(s.doC()); | |
| 468 | try expect(S.do(&s)); | |
| 469 | try expect(s.do()); | |
| 470 | } | |
| 471 | ||
| 472 | test "implicit cast packed struct field to const ptr" { | |
| 473 | const LevelUpMove = packed struct { | |
| 474 | move_id: u9, | |
| 475 | level: u7, | |
| 476 | ||
| 477 | fn toInt(value: u7) u7 { | |
| 478 | return value; | |
| 479 | } | |
| 480 | }; | |
| 481 | ||
| 482 | var lup: LevelUpMove = undefined; | |
| 483 | lup.level = 12; | |
| 484 | const res = LevelUpMove.toInt(lup.level); | |
| 485 | try expect(res == 12); | |
| 486 | } | |
| 487 | ||
| 488 | test "pointer to packed struct member in a stack variable" { | |
| 489 | const S = packed struct { | |
| 490 | a: u2, | |
| 491 | b: u2, | |
| 492 | }; | |
| 493 | ||
| 494 | var s = S{ .a = 2, .b = 0 }; | |
| 495 | var b_ptr = &s.b; | |
| 496 | try expect(s.b == 0); | |
| 497 | b_ptr.* = 2; | |
| 498 | try expect(s.b == 2); | |
| 499 | } | |
| 500 | ||
| 501 | test "non-byte-aligned array inside packed struct" { | |
| 502 | const Foo = packed struct { | |
| 503 | a: bool, | |
| 504 | b: [0x16]u8, | |
| 505 | }; | |
| 506 | const S = struct { | |
| 507 | fn bar(slice: []const u8) !void { | |
| 508 | try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); | |
| 509 | } | |
| 510 | fn doTheTest() !void { | |
| 511 | var foo = Foo{ | |
| 512 | .a = true, | |
| 513 | .b = "abcdefghijklmnopqurstu".*, | |
| 514 | }; | |
| 515 | const value = foo.b; | |
| 516 | try bar(&value); | |
| 517 | } | |
| 518 | }; | |
| 519 | try S.doTheTest(); | |
| 520 | comptime try S.doTheTest(); | |
| 521 | } | |
| 522 | ||
| 523 | test "packed struct with u0 field access" { | |
| 524 | const S = packed struct { | |
| 525 | f0: u0, | |
| 526 | }; | |
| 527 | var s = S{ .f0 = 0 }; | |
| 528 | comptime try expect(s.f0 == 0); | |
| 529 | } | |
| 530 | ||
| 531 | const S0 = struct { | |
| 532 | bar: S1, | |
| 533 | ||
| 534 | pub const S1 = struct { | |
| 535 | value: u8, | |
| 536 | }; | |
| 537 | ||
| 538 | fn init() @This() { | |
| 539 | return S0{ .bar = S1{ .value = 123 } }; | |
| 540 | } | |
| 541 | }; | |
| 542 | ||
| 543 | var g_foo: S0 = S0.init(); | |
| 544 | ||
| 545 | test "access to global struct fields" { | |
| 546 | g_foo.bar.value = 42; | |
| 547 | try expect(g_foo.bar.value == 42); | |
| 548 | } | |
| 549 | ||
| 550 | test "packed struct with fp fields" { | |
| 551 | const S = packed struct { | |
| 552 | data: [3]f32, | |
| 553 | ||
| 554 | pub fn frob(self: *@This()) void { | |
| 555 | self.data[0] += self.data[1] + self.data[2]; | |
| 556 | self.data[1] += self.data[0] + self.data[2]; | |
| 557 | self.data[2] += self.data[0] + self.data[1]; | |
| 558 | } | |
| 559 | }; | |
| 560 | ||
| 561 | var s: S = undefined; | |
| 562 | s.data[0] = 1.0; | |
| 563 | s.data[1] = 2.0; | |
| 564 | s.data[2] = 3.0; | |
| 565 | s.frob(); | |
| 566 | try expectEqual(@as(f32, 6.0), s.data[0]); | |
| 567 | try expectEqual(@as(f32, 11.0), s.data[1]); | |
| 568 | try expectEqual(@as(f32, 20.0), s.data[2]); | |
| 569 | } | |
| 570 | ||
| 571 | test "use within struct scope" { | |
| 572 | const S = struct { | |
| 573 | usingnamespace struct { | |
| 574 | pub fn inner() i32 { | |
| 575 | return 42; | |
| 576 | } | |
| 577 | }; | |
| 578 | }; | |
| 579 | try expectEqual(@as(i32, 42), S.inner()); | |
| 580 | } | |
| 581 | ||
| 582 | test "default struct initialization fields" { | |
| 583 | const S = struct { | |
| 584 | a: i32 = 1234, | |
| 585 | b: i32, | |
| 586 | }; | |
| 587 | const x = S{ | |
| 588 | .b = 5, | |
| 589 | }; | |
| 590 | var five: i32 = 5; | |
| 591 | const y = S{ | |
| 592 | .b = five, | |
| 593 | }; | |
| 594 | if (x.a + x.b != 1239) { | |
| 595 | @compileError("it should be comptime known"); | |
| 596 | } | |
| 597 | try expectEqual(y, x); | |
| 598 | try expectEqual(1239, x.a + x.b); | |
| 599 | } | |
| 600 | ||
| 601 | test "fn with C calling convention returns struct by value" { | |
| 602 | const S = struct { | |
| 603 | fn entry() !void { | |
| 604 | var x = makeBar(10); | |
| 605 | try expectEqual(@as(i32, 10), x.handle); | |
| 606 | } | |
| 607 | ||
| 608 | const ExternBar = extern struct { | |
| 609 | handle: i32, | |
| 610 | }; | |
| 611 | ||
| 612 | fn makeBar(t: i32) callconv(.C) ExternBar { | |
| 613 | return ExternBar{ | |
| 614 | .handle = t, | |
| 615 | }; | |
| 616 | } | |
| 617 | }; | |
| 618 | try S.entry(); | |
| 619 | comptime try S.entry(); | |
| 620 | } | |
| 621 | ||
| 622 | test "for loop over pointers to struct, getting field from struct pointer" { | |
| 623 | const S = struct { | |
| 624 | const Foo = struct { | |
| 625 | name: []const u8, | |
| 626 | }; | |
| 627 | ||
| 628 | var ok = true; | |
| 629 | ||
| 630 | fn eql(a: []const u8) bool { | |
| 631 | _ = a; | |
| 632 | return true; | |
| 633 | } | |
| 634 | ||
| 635 | const ArrayList = struct { | |
| 636 | fn toSlice(self: *ArrayList) []*Foo { | |
| 637 | _ = self; | |
| 638 | return @as([*]*Foo, undefined)[0..0]; | |
| 639 | } | |
| 640 | }; | |
| 641 | ||
| 642 | fn doTheTest() !void { | |
| 643 | var objects: ArrayList = undefined; | |
| 644 | ||
| 645 | for (objects.toSlice()) |obj| { | |
| 646 | if (eql(obj.name)) { | |
| 647 | ok = false; | |
| 648 | } | |
| 649 | } | |
| 650 | ||
| 651 | try expect(ok); | |
| 652 | } | |
| 653 | }; | |
| 654 | try S.doTheTest(); | |
| 655 | } | |
| 656 | ||
| 657 | test "zero-bit field in packed struct" { | |
| 658 | const S = packed struct { | |
| 659 | x: u10, | |
| 660 | y: void, | |
| 661 | }; | |
| 662 | var x: S = undefined; | |
| 663 | _ = x; | |
| 664 | } | |
| 665 | ||
| 666 | test "struct field init with catch" { | |
| 667 | const S = struct { | |
| 668 | fn doTheTest() !void { | |
| 669 | var x: anyerror!isize = 1; | |
| 670 | var req = Foo{ | |
| 671 | .field = x catch undefined, | |
| 672 | }; | |
| 673 | try expect(req.field == 1); | |
| 674 | } | |
| 675 | ||
| 676 | pub const Foo = extern struct { | |
| 677 | field: isize, | |
| 678 | }; | |
| 679 | }; | |
| 680 | try S.doTheTest(); | |
| 681 | comptime try S.doTheTest(); | |
| 682 | } | |
| 683 | ||
| 684 | test "packed struct with non-ABI-aligned field" { | |
| 685 | const S = packed struct { | |
| 686 | x: u9, | |
| 687 | y: u183, | |
| 688 | }; | |
| 689 | var s: S = undefined; | |
| 690 | s.x = 1; | |
| 691 | s.y = 42; | |
| 692 | try expect(s.x == 1); | |
| 693 | try expect(s.y == 42); | |
| 694 | } | |
| 695 | ||
| 696 | test "non-packed struct with u128 entry in union" { | |
| 697 | const U = union(enum) { | |
| 698 | Num: u128, | |
| 699 | Void, | |
| 700 | }; | |
| 701 | ||
| 702 | const S = struct { | |
| 703 | f1: U, | |
| 704 | f2: U, | |
| 705 | }; | |
| 706 | ||
| 707 | var sx: S = undefined; | |
| 708 | var s = &sx; | |
| 709 | try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); | |
| 710 | var v2 = U{ .Num = 123 }; | |
| 711 | s.f2 = v2; | |
| 712 | try std.testing.expect(s.f2.Num == 123); | |
| 713 | } | |
| 714 | ||
| 715 | test "packed struct field passed to generic function" { | |
| 716 | const S = struct { | |
| 717 | const P = packed struct { | |
| 718 | b: u5, | |
| 719 | g: u5, | |
| 720 | r: u5, | |
| 721 | a: u1, | |
| 722 | }; | |
| 723 | ||
| 724 | fn genericReadPackedField(ptr: anytype) u5 { | |
| 725 | return ptr.*; | |
| 726 | } | |
| 727 | }; | |
| 728 | ||
| 729 | var p: S.P = undefined; | |
| 730 | p.b = 29; | |
| 731 | var loaded = S.genericReadPackedField(&p.b); | |
| 732 | try expect(loaded == 29); | |
| 733 | } | |
| 734 | ||
| 735 | test "anonymous struct literal syntax" { | |
| 736 | const S = struct { | |
| 737 | const Point = struct { | |
| 738 | x: i32, | |
| 739 | y: i32, | |
| 740 | }; | |
| 741 | ||
| 742 | fn doTheTest() !void { | |
| 743 | var p: Point = .{ | |
| 744 | .x = 1, | |
| 745 | .y = 2, | |
| 746 | }; | |
| 747 | try expect(p.x == 1); | |
| 748 | try expect(p.y == 2); | |
| 749 | } | |
| 750 | }; | |
| 751 | try S.doTheTest(); | |
| 752 | comptime try S.doTheTest(); | |
| 753 | } | |
| 754 | ||
| 755 | test "fully anonymous struct" { | |
| 756 | const S = struct { | |
| 757 | fn doTheTest() !void { | |
| 758 | try dump(.{ | |
| 759 | .int = @as(u32, 1234), | |
| 760 | .float = @as(f64, 12.34), | |
| 761 | .b = true, | |
| 762 | .s = "hi", | |
| 763 | }); | |
| 764 | } | |
| 765 | fn dump(args: anytype) !void { | |
| 766 | try expect(args.int == 1234); | |
| 767 | try expect(args.float == 12.34); | |
| 768 | try expect(args.b); | |
| 769 | try expect(args.s[0] == 'h'); | |
| 770 | try expect(args.s[1] == 'i'); | |
| 771 | } | |
| 772 | }; | |
| 773 | try S.doTheTest(); | |
| 774 | comptime try S.doTheTest(); | |
| 775 | } | |
| 776 | ||
| 777 | test "fully anonymous list literal" { | |
| 778 | const S = struct { | |
| 779 | fn doTheTest() !void { | |
| 780 | try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); | |
| 781 | } | |
| 782 | fn dump(args: anytype) !void { | |
| 783 | try expect(args.@"0" == 1234); | |
| 784 | try expect(args.@"1" == 12.34); | |
| 785 | try expect(args.@"2"); | |
| 786 | try expect(args.@"3"[0] == 'h'); | |
| 787 | try expect(args.@"3"[1] == 'i'); | |
| 788 | } | |
| 789 | }; | |
| 790 | try S.doTheTest(); | |
| 791 | comptime try S.doTheTest(); | |
| 792 | } | |
| 793 | ||
| 794 | test "anonymous struct literal assigned to variable" { | |
| 795 | var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; | |
| 796 | try expect(vec.@"0" == 22); | |
| 797 | try expect(vec.@"1" == 55); | |
| 798 | try expect(vec.@"2" == 99); | |
| 799 | } | |
| 800 | ||
| 801 | test "struct with var field" { | |
| 802 | const Point = struct { | |
| 803 | x: anytype, | |
| 804 | y: anytype, | |
| 805 | }; | |
| 806 | const pt = Point{ | |
| 807 | .x = 1, | |
| 808 | .y = 2, | |
| 809 | }; | |
| 810 | try expect(pt.x == 1); | |
| 811 | try expect(pt.y == 2); | |
| 812 | } | |
| 813 | ||
| 814 | test "comptime struct field" { | |
| 815 | const T = struct { | |
| 816 | a: i32, | |
| 817 | comptime b: i32 = 1234, | |
| 818 | }; | |
| 819 | ||
| 820 | var foo: T = undefined; | |
| 821 | comptime try expect(foo.b == 1234); | |
| 822 | } | |
| 823 | ||
| 824 | test "anon struct literal field value initialized with fn call" { | |
| 825 | const S = struct { | |
| 826 | fn doTheTest() !void { | |
| 827 | var x = .{foo()}; | |
| 828 | try expectEqualSlices(u8, x[0], "hi"); | |
| 829 | } | |
| 830 | fn foo() []const u8 { | |
| 831 | return "hi"; | |
| 832 | } | |
| 833 | }; | |
| 834 | try S.doTheTest(); | |
| 835 | comptime try S.doTheTest(); | |
| 836 | } | |
| 837 | ||
| 838 | test "self-referencing struct via array member" { | |
| 839 | const T = struct { | |
| 840 | children: [1]*@This(), | |
| 841 | }; | |
| 842 | var x: T = undefined; | |
| 843 | x = T{ .children = .{&x} }; | |
| 844 | try expect(x.children[0] == &x); | |
| 845 | } | |
| 846 | ||
| 847 | test "struct with union field" { | |
| 848 | const Value = struct { | |
| 849 | ref: u32 = 2, | |
| 850 | kind: union(enum) { | |
| 851 | None: usize, | |
| 852 | Bool: bool, | |
| 853 | }, | |
| 854 | }; | |
| 855 | ||
| 856 | var True = Value{ | |
| 857 | .kind = .{ .Bool = true }, | |
| 858 | }; | |
| 859 | try expectEqual(@as(u32, 2), True.ref); | |
| 860 | try expectEqual(true, True.kind.Bool); | |
| 861 | } | |
| 862 | ||
| 863 | test "type coercion of anon struct literal to struct" { | |
| 864 | const S = struct { | |
| 865 | const S2 = struct { | |
| 866 | A: u32, | |
| 867 | B: []const u8, | |
| 868 | C: void, | |
| 869 | D: Foo = .{}, | |
| 870 | }; | |
| 871 | ||
| 872 | const Foo = struct { | |
| 873 | field: i32 = 1234, | |
| 874 | }; | |
| 875 | ||
| 876 | fn doTheTest() !void { | |
| 877 | var y: u32 = 42; | |
| 878 | const t0 = .{ .A = 123, .B = "foo", .C = {} }; | |
| 879 | const t1 = .{ .A = y, .B = "foo", .C = {} }; | |
| 880 | const y0: S2 = t0; | |
| 881 | var y1: S2 = t1; | |
| 882 | try expect(y0.A == 123); | |
| 883 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 884 | try expect(y0.C == {}); | |
| 885 | try expect(y0.D.field == 1234); | |
| 886 | try expect(y1.A == y); | |
| 887 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 888 | try expect(y1.C == {}); | |
| 889 | try expect(y1.D.field == 1234); | |
| 890 | } | |
| 891 | }; | |
| 892 | try S.doTheTest(); | |
| 893 | comptime try S.doTheTest(); | |
| 894 | } | |
| 895 | ||
| 896 | test "type coercion of pointer to anon struct literal to pointer to struct" { | |
| 897 | const S = struct { | |
| 898 | const S2 = struct { | |
| 899 | A: u32, | |
| 900 | B: []const u8, | |
| 901 | C: void, | |
| 902 | D: Foo = .{}, | |
| 903 | }; | |
| 904 | ||
| 905 | const Foo = struct { | |
| 906 | field: i32 = 1234, | |
| 907 | }; | |
| 26 | const empty_global_instance = StructWithNoFields{}; | |
| 908 | 27 | |
| 909 | fn doTheTest() !void { | |
| 910 | var y: u32 = 42; | |
| 911 | const t0 = &.{ .A = 123, .B = "foo", .C = {} }; | |
| 912 | const t1 = &.{ .A = y, .B = "foo", .C = {} }; | |
| 913 | const y0: *const S2 = t0; | |
| 914 | var y1: *const S2 = t1; | |
| 915 | try expect(y0.A == 123); | |
| 916 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 917 | try expect(y0.C == {}); | |
| 918 | try expect(y0.D.field == 1234); | |
| 919 | try expect(y1.A == y); | |
| 920 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 921 | try expect(y1.C == {}); | |
| 922 | try expect(y1.D.field == 1234); | |
| 923 | } | |
| 924 | }; | |
| 925 | try S.doTheTest(); | |
| 926 | comptime try S.doTheTest(); | |
| 28 | test "return empty struct instance" { | |
| 29 | _ = returnEmptyStructInstance(); | |
| 927 | 30 | } |
| 928 | ||
| 929 | test "packed struct with undefined initializers" { | |
| 930 | const S = struct { | |
| 931 | const P = packed struct { | |
| 932 | a: u3, | |
| 933 | _a: u3 = undefined, | |
| 934 | b: u3, | |
| 935 | _b: u3 = undefined, | |
| 936 | c: u3, | |
| 937 | _c: u3 = undefined, | |
| 938 | }; | |
| 939 | ||
| 940 | fn doTheTest() !void { | |
| 941 | var p: P = undefined; | |
| 942 | p = P{ .a = 2, .b = 4, .c = 6 }; | |
| 943 | // Make sure the compiler doesn't touch the unprefixed fields. | |
| 944 | // Use expect since i386-linux doesn't like expectEqual | |
| 945 | try expect(p.a == 2); | |
| 946 | try expect(p.b == 4); | |
| 947 | try expect(p.c == 6); | |
| 948 | } | |
| 949 | }; | |
| 950 | ||
| 951 | try S.doTheTest(); | |
| 952 | comptime try S.doTheTest(); | |
| 31 | fn returnEmptyStructInstance() StructWithNoFields { | |
| 32 | return empty_global_instance; | |
| 953 | 33 | } |
test/behavior/struct_stage1.zig created+928| ... | ... | @@ -0,0 +1,928 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const native_endian = builtin.target.cpu.arch.endian(); | |
| 4 | const expect = std.testing.expect; | |
| 5 | const expectEqual = std.testing.expectEqual; | |
| 6 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 7 | const maxInt = std.math.maxInt; | |
| 8 | top_level_field: i32, | |
| 9 | ||
| 10 | test "top level fields" { | |
| 11 | var instance = @This(){ | |
| 12 | .top_level_field = 1234, | |
| 13 | }; | |
| 14 | instance.top_level_field += 1; | |
| 15 | try expectEqual(@as(i32, 1235), instance.top_level_field); | |
| 16 | } | |
| 17 | ||
| 18 | test "void struct fields" { | |
| 19 | const foo = VoidStructFieldsFoo{ | |
| 20 | .a = void{}, | |
| 21 | .b = 1, | |
| 22 | .c = void{}, | |
| 23 | }; | |
| 24 | try expect(foo.b == 1); | |
| 25 | try expect(@sizeOf(VoidStructFieldsFoo) == 4); | |
| 26 | } | |
| 27 | const VoidStructFieldsFoo = struct { | |
| 28 | a: void, | |
| 29 | b: i32, | |
| 30 | c: void, | |
| 31 | }; | |
| 32 | ||
| 33 | test "structs" { | |
| 34 | var foo: StructFoo = undefined; | |
| 35 | @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); | |
| 36 | foo.a += 1; | |
| 37 | foo.b = foo.a == 1; | |
| 38 | try testFoo(foo); | |
| 39 | testMutation(&foo); | |
| 40 | try expect(foo.c == 100); | |
| 41 | } | |
| 42 | const StructFoo = struct { | |
| 43 | a: i32, | |
| 44 | b: bool, | |
| 45 | c: f32, | |
| 46 | }; | |
| 47 | fn testFoo(foo: StructFoo) !void { | |
| 48 | try expect(foo.b); | |
| 49 | } | |
| 50 | fn testMutation(foo: *StructFoo) void { | |
| 51 | foo.c = 100; | |
| 52 | } | |
| 53 | ||
| 54 | const Node = struct { | |
| 55 | val: Val, | |
| 56 | next: *Node, | |
| 57 | }; | |
| 58 | ||
| 59 | const Val = struct { | |
| 60 | x: i32, | |
| 61 | }; | |
| 62 | ||
| 63 | test "struct point to self" { | |
| 64 | var root: Node = undefined; | |
| 65 | root.val.x = 1; | |
| 66 | ||
| 67 | var node: Node = undefined; | |
| 68 | node.next = &root; | |
| 69 | node.val.x = 2; | |
| 70 | ||
| 71 | root.next = &node; | |
| 72 | ||
| 73 | try expect(node.next.next.next.val.x == 1); | |
| 74 | } | |
| 75 | ||
| 76 | test "struct byval assign" { | |
| 77 | var foo1: StructFoo = undefined; | |
| 78 | var foo2: StructFoo = undefined; | |
| 79 | ||
| 80 | foo1.a = 1234; | |
| 81 | foo2.a = 0; | |
| 82 | try expect(foo2.a == 0); | |
| 83 | foo2 = foo1; | |
| 84 | try expect(foo2.a == 1234); | |
| 85 | } | |
| 86 | ||
| 87 | fn structInitializer() void { | |
| 88 | const val = Val{ .x = 42 }; | |
| 89 | try expect(val.x == 42); | |
| 90 | } | |
| 91 | ||
| 92 | test "fn call of struct field" { | |
| 93 | const Foo = struct { | |
| 94 | ptr: fn () i32, | |
| 95 | }; | |
| 96 | const S = struct { | |
| 97 | fn aFunc() i32 { | |
| 98 | return 13; | |
| 99 | } | |
| 100 | ||
| 101 | fn callStructField(foo: Foo) i32 { | |
| 102 | return foo.ptr(); | |
| 103 | } | |
| 104 | }; | |
| 105 | ||
| 106 | try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); | |
| 107 | } | |
| 108 | ||
| 109 | test "store member function in variable" { | |
| 110 | const instance = MemberFnTestFoo{ .x = 1234 }; | |
| 111 | const memberFn = MemberFnTestFoo.member; | |
| 112 | const result = memberFn(instance); | |
| 113 | try expect(result == 1234); | |
| 114 | } | |
| 115 | const MemberFnTestFoo = struct { | |
| 116 | x: i32, | |
| 117 | fn member(foo: MemberFnTestFoo) i32 { | |
| 118 | return foo.x; | |
| 119 | } | |
| 120 | }; | |
| 121 | ||
| 122 | test "call member function directly" { | |
| 123 | const instance = MemberFnTestFoo{ .x = 1234 }; | |
| 124 | const result = MemberFnTestFoo.member(instance); | |
| 125 | try expect(result == 1234); | |
| 126 | } | |
| 127 | ||
| 128 | test "member functions" { | |
| 129 | const r = MemberFnRand{ .seed = 1234 }; | |
| 130 | try expect(r.getSeed() == 1234); | |
| 131 | } | |
| 132 | const MemberFnRand = struct { | |
| 133 | seed: u32, | |
| 134 | pub fn getSeed(r: *const MemberFnRand) u32 { | |
| 135 | return r.seed; | |
| 136 | } | |
| 137 | }; | |
| 138 | ||
| 139 | test "return struct byval from function" { | |
| 140 | const bar = makeBar2(1234, 5678); | |
| 141 | try expect(bar.y == 5678); | |
| 142 | } | |
| 143 | const Bar = struct { | |
| 144 | x: i32, | |
| 145 | y: i32, | |
| 146 | }; | |
| 147 | fn makeBar2(x: i32, y: i32) Bar { | |
| 148 | return Bar{ | |
| 149 | .x = x, | |
| 150 | .y = y, | |
| 151 | }; | |
| 152 | } | |
| 153 | ||
| 154 | test "empty struct method call" { | |
| 155 | const es = EmptyStruct{}; | |
| 156 | try expect(es.method() == 1234); | |
| 157 | } | |
| 158 | const EmptyStruct = struct { | |
| 159 | fn method(es: *const EmptyStruct) i32 { | |
| 160 | _ = es; | |
| 161 | return 1234; | |
| 162 | } | |
| 163 | }; | |
| 164 | ||
| 165 | test "return empty struct from fn" { | |
| 166 | _ = testReturnEmptyStructFromFn(); | |
| 167 | } | |
| 168 | const EmptyStruct2 = struct {}; | |
| 169 | fn testReturnEmptyStructFromFn() EmptyStruct2 { | |
| 170 | return EmptyStruct2{}; | |
| 171 | } | |
| 172 | ||
| 173 | test "pass slice of empty struct to fn" { | |
| 174 | try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); | |
| 175 | } | |
| 176 | fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { | |
| 177 | return slice.len; | |
| 178 | } | |
| 179 | ||
| 180 | const APackedStruct = packed struct { | |
| 181 | x: u8, | |
| 182 | y: u8, | |
| 183 | }; | |
| 184 | ||
| 185 | test "packed struct" { | |
| 186 | var foo = APackedStruct{ | |
| 187 | .x = 1, | |
| 188 | .y = 2, | |
| 189 | }; | |
| 190 | foo.y += 1; | |
| 191 | const four = foo.x + foo.y; | |
| 192 | try expect(four == 4); | |
| 193 | } | |
| 194 | ||
| 195 | const BitField1 = packed struct { | |
| 196 | a: u3, | |
| 197 | b: u3, | |
| 198 | c: u2, | |
| 199 | }; | |
| 200 | ||
| 201 | const bit_field_1 = BitField1{ | |
| 202 | .a = 1, | |
| 203 | .b = 2, | |
| 204 | .c = 3, | |
| 205 | }; | |
| 206 | ||
| 207 | test "bit field access" { | |
| 208 | var data = bit_field_1; | |
| 209 | try expect(getA(&data) == 1); | |
| 210 | try expect(getB(&data) == 2); | |
| 211 | try expect(getC(&data) == 3); | |
| 212 | comptime try expect(@sizeOf(BitField1) == 1); | |
| 213 | ||
| 214 | data.b += 1; | |
| 215 | try expect(data.b == 3); | |
| 216 | ||
| 217 | data.a += 1; | |
| 218 | try expect(data.a == 2); | |
| 219 | try expect(data.b == 3); | |
| 220 | } | |
| 221 | ||
| 222 | fn getA(data: *const BitField1) u3 { | |
| 223 | return data.a; | |
| 224 | } | |
| 225 | ||
| 226 | fn getB(data: *const BitField1) u3 { | |
| 227 | return data.b; | |
| 228 | } | |
| 229 | ||
| 230 | fn getC(data: *const BitField1) u2 { | |
| 231 | return data.c; | |
| 232 | } | |
| 233 | ||
| 234 | const Foo24Bits = packed struct { | |
| 235 | field: u24, | |
| 236 | }; | |
| 237 | const Foo96Bits = packed struct { | |
| 238 | a: u24, | |
| 239 | b: u24, | |
| 240 | c: u24, | |
| 241 | d: u24, | |
| 242 | }; | |
| 243 | ||
| 244 | test "packed struct 24bits" { | |
| 245 | comptime { | |
| 246 | try expect(@sizeOf(Foo24Bits) == 4); | |
| 247 | if (@sizeOf(usize) == 4) { | |
| 248 | try expect(@sizeOf(Foo96Bits) == 12); | |
| 249 | } else { | |
| 250 | try expect(@sizeOf(Foo96Bits) == 16); | |
| 251 | } | |
| 252 | } | |
| 253 | ||
| 254 | var value = Foo96Bits{ | |
| 255 | .a = 0, | |
| 256 | .b = 0, | |
| 257 | .c = 0, | |
| 258 | .d = 0, | |
| 259 | }; | |
| 260 | value.a += 1; | |
| 261 | try expect(value.a == 1); | |
| 262 | try expect(value.b == 0); | |
| 263 | try expect(value.c == 0); | |
| 264 | try expect(value.d == 0); | |
| 265 | ||
| 266 | value.b += 1; | |
| 267 | try expect(value.a == 1); | |
| 268 | try expect(value.b == 1); | |
| 269 | try expect(value.c == 0); | |
| 270 | try expect(value.d == 0); | |
| 271 | ||
| 272 | value.c += 1; | |
| 273 | try expect(value.a == 1); | |
| 274 | try expect(value.b == 1); | |
| 275 | try expect(value.c == 1); | |
| 276 | try expect(value.d == 0); | |
| 277 | ||
| 278 | value.d += 1; | |
| 279 | try expect(value.a == 1); | |
| 280 | try expect(value.b == 1); | |
| 281 | try expect(value.c == 1); | |
| 282 | try expect(value.d == 1); | |
| 283 | } | |
| 284 | ||
| 285 | const Foo32Bits = packed struct { | |
| 286 | field: u24, | |
| 287 | pad: u8, | |
| 288 | }; | |
| 289 | ||
| 290 | const FooArray24Bits = packed struct { | |
| 291 | a: u16, | |
| 292 | b: [2]Foo32Bits, | |
| 293 | c: u16, | |
| 294 | }; | |
| 295 | ||
| 296 | // TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 | |
| 297 | test "packed array 24bits" { | |
| 298 | comptime { | |
| 299 | try expect(@sizeOf([9]Foo32Bits) == 9 * 4); | |
| 300 | try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); | |
| 301 | } | |
| 302 | ||
| 303 | var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); | |
| 304 | bytes[bytes.len - 1] = 0xaa; | |
| 305 | const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; | |
| 306 | try expect(ptr.a == 0); | |
| 307 | try expect(ptr.b[0].field == 0); | |
| 308 | try expect(ptr.b[1].field == 0); | |
| 309 | try expect(ptr.c == 0); | |
| 310 | ||
| 311 | ptr.a = maxInt(u16); | |
| 312 | try expect(ptr.a == maxInt(u16)); | |
| 313 | try expect(ptr.b[0].field == 0); | |
| 314 | try expect(ptr.b[1].field == 0); | |
| 315 | try expect(ptr.c == 0); | |
| 316 | ||
| 317 | ptr.b[0].field = maxInt(u24); | |
| 318 | try expect(ptr.a == maxInt(u16)); | |
| 319 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 320 | try expect(ptr.b[1].field == 0); | |
| 321 | try expect(ptr.c == 0); | |
| 322 | ||
| 323 | ptr.b[1].field = maxInt(u24); | |
| 324 | try expect(ptr.a == maxInt(u16)); | |
| 325 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 326 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 327 | try expect(ptr.c == 0); | |
| 328 | ||
| 329 | ptr.c = maxInt(u16); | |
| 330 | try expect(ptr.a == maxInt(u16)); | |
| 331 | try expect(ptr.b[0].field == maxInt(u24)); | |
| 332 | try expect(ptr.b[1].field == maxInt(u24)); | |
| 333 | try expect(ptr.c == maxInt(u16)); | |
| 334 | ||
| 335 | try expect(bytes[bytes.len - 1] == 0xaa); | |
| 336 | } | |
| 337 | ||
| 338 | const FooStructAligned = packed struct { | |
| 339 | a: u8, | |
| 340 | b: u8, | |
| 341 | }; | |
| 342 | ||
| 343 | const FooArrayOfAligned = packed struct { | |
| 344 | a: [2]FooStructAligned, | |
| 345 | }; | |
| 346 | ||
| 347 | test "aligned array of packed struct" { | |
| 348 | comptime { | |
| 349 | try expect(@sizeOf(FooStructAligned) == 2); | |
| 350 | try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); | |
| 351 | } | |
| 352 | ||
| 353 | var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); | |
| 354 | const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; | |
| 355 | ||
| 356 | try expect(ptr.a[0].a == 0xbb); | |
| 357 | try expect(ptr.a[0].b == 0xbb); | |
| 358 | try expect(ptr.a[1].a == 0xbb); | |
| 359 | try expect(ptr.a[1].b == 0xbb); | |
| 360 | } | |
| 361 | ||
| 362 | test "runtime struct initialization of bitfield" { | |
| 363 | const s1 = Nibbles{ | |
| 364 | .x = x1, | |
| 365 | .y = x1, | |
| 366 | }; | |
| 367 | const s2 = Nibbles{ | |
| 368 | .x = @intCast(u4, x2), | |
| 369 | .y = @intCast(u4, x2), | |
| 370 | }; | |
| 371 | ||
| 372 | try expect(s1.x == x1); | |
| 373 | try expect(s1.y == x1); | |
| 374 | try expect(s2.x == @intCast(u4, x2)); | |
| 375 | try expect(s2.y == @intCast(u4, x2)); | |
| 376 | } | |
| 377 | ||
| 378 | var x1 = @as(u4, 1); | |
| 379 | var x2 = @as(u8, 2); | |
| 380 | ||
| 381 | const Nibbles = packed struct { | |
| 382 | x: u4, | |
| 383 | y: u4, | |
| 384 | }; | |
| 385 | ||
| 386 | const Bitfields = packed struct { | |
| 387 | f1: u16, | |
| 388 | f2: u16, | |
| 389 | f3: u8, | |
| 390 | f4: u8, | |
| 391 | f5: u4, | |
| 392 | f6: u4, | |
| 393 | f7: u8, | |
| 394 | }; | |
| 395 | ||
| 396 | test "native bit field understands endianness" { | |
| 397 | var all: u64 = if (native_endian != .Little) | |
| 398 | 0x1111222233445677 | |
| 399 | else | |
| 400 | 0x7765443322221111; | |
| 401 | var bytes: [8]u8 = undefined; | |
| 402 | @memcpy(&bytes, @ptrCast([*]u8, &all), 8); | |
| 403 | var bitfields = @ptrCast(*Bitfields, &bytes).*; | |
| 404 | ||
| 405 | try expect(bitfields.f1 == 0x1111); | |
| 406 | try expect(bitfields.f2 == 0x2222); | |
| 407 | try expect(bitfields.f3 == 0x33); | |
| 408 | try expect(bitfields.f4 == 0x44); | |
| 409 | try expect(bitfields.f5 == 0x5); | |
| 410 | try expect(bitfields.f6 == 0x6); | |
| 411 | try expect(bitfields.f7 == 0x77); | |
| 412 | } | |
| 413 | ||
| 414 | test "align 1 field before self referential align 8 field as slice return type" { | |
| 415 | const result = alloc(Expr); | |
| 416 | try expect(result.len == 0); | |
| 417 | } | |
| 418 | ||
| 419 | const Expr = union(enum) { | |
| 420 | Literal: u8, | |
| 421 | Question: *Expr, | |
| 422 | }; | |
| 423 | ||
| 424 | fn alloc(comptime T: type) []T { | |
| 425 | return &[_]T{}; | |
| 426 | } | |
| 427 | ||
| 428 | test "call method with mutable reference to struct with no fields" { | |
| 429 | const S = struct { | |
| 430 | fn doC(s: *const @This()) bool { | |
| 431 | _ = s; | |
| 432 | return true; | |
| 433 | } | |
| 434 | fn do(s: *@This()) bool { | |
| 435 | _ = s; | |
| 436 | return true; | |
| 437 | } | |
| 438 | }; | |
| 439 | ||
| 440 | var s = S{}; | |
| 441 | try expect(S.doC(&s)); | |
| 442 | try expect(s.doC()); | |
| 443 | try expect(S.do(&s)); | |
| 444 | try expect(s.do()); | |
| 445 | } | |
| 446 | ||
| 447 | test "implicit cast packed struct field to const ptr" { | |
| 448 | const LevelUpMove = packed struct { | |
| 449 | move_id: u9, | |
| 450 | level: u7, | |
| 451 | ||
| 452 | fn toInt(value: u7) u7 { | |
| 453 | return value; | |
| 454 | } | |
| 455 | }; | |
| 456 | ||
| 457 | var lup: LevelUpMove = undefined; | |
| 458 | lup.level = 12; | |
| 459 | const res = LevelUpMove.toInt(lup.level); | |
| 460 | try expect(res == 12); | |
| 461 | } | |
| 462 | ||
| 463 | test "pointer to packed struct member in a stack variable" { | |
| 464 | const S = packed struct { | |
| 465 | a: u2, | |
| 466 | b: u2, | |
| 467 | }; | |
| 468 | ||
| 469 | var s = S{ .a = 2, .b = 0 }; | |
| 470 | var b_ptr = &s.b; | |
| 471 | try expect(s.b == 0); | |
| 472 | b_ptr.* = 2; | |
| 473 | try expect(s.b == 2); | |
| 474 | } | |
| 475 | ||
| 476 | test "non-byte-aligned array inside packed struct" { | |
| 477 | const Foo = packed struct { | |
| 478 | a: bool, | |
| 479 | b: [0x16]u8, | |
| 480 | }; | |
| 481 | const S = struct { | |
| 482 | fn bar(slice: []const u8) !void { | |
| 483 | try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); | |
| 484 | } | |
| 485 | fn doTheTest() !void { | |
| 486 | var foo = Foo{ | |
| 487 | .a = true, | |
| 488 | .b = "abcdefghijklmnopqurstu".*, | |
| 489 | }; | |
| 490 | const value = foo.b; | |
| 491 | try bar(&value); | |
| 492 | } | |
| 493 | }; | |
| 494 | try S.doTheTest(); | |
| 495 | comptime try S.doTheTest(); | |
| 496 | } | |
| 497 | ||
| 498 | test "packed struct with u0 field access" { | |
| 499 | const S = packed struct { | |
| 500 | f0: u0, | |
| 501 | }; | |
| 502 | var s = S{ .f0 = 0 }; | |
| 503 | comptime try expect(s.f0 == 0); | |
| 504 | } | |
| 505 | ||
| 506 | const S0 = struct { | |
| 507 | bar: S1, | |
| 508 | ||
| 509 | pub const S1 = struct { | |
| 510 | value: u8, | |
| 511 | }; | |
| 512 | ||
| 513 | fn init() @This() { | |
| 514 | return S0{ .bar = S1{ .value = 123 } }; | |
| 515 | } | |
| 516 | }; | |
| 517 | ||
| 518 | var g_foo: S0 = S0.init(); | |
| 519 | ||
| 520 | test "access to global struct fields" { | |
| 521 | g_foo.bar.value = 42; | |
| 522 | try expect(g_foo.bar.value == 42); | |
| 523 | } | |
| 524 | ||
| 525 | test "packed struct with fp fields" { | |
| 526 | const S = packed struct { | |
| 527 | data: [3]f32, | |
| 528 | ||
| 529 | pub fn frob(self: *@This()) void { | |
| 530 | self.data[0] += self.data[1] + self.data[2]; | |
| 531 | self.data[1] += self.data[0] + self.data[2]; | |
| 532 | self.data[2] += self.data[0] + self.data[1]; | |
| 533 | } | |
| 534 | }; | |
| 535 | ||
| 536 | var s: S = undefined; | |
| 537 | s.data[0] = 1.0; | |
| 538 | s.data[1] = 2.0; | |
| 539 | s.data[2] = 3.0; | |
| 540 | s.frob(); | |
| 541 | try expectEqual(@as(f32, 6.0), s.data[0]); | |
| 542 | try expectEqual(@as(f32, 11.0), s.data[1]); | |
| 543 | try expectEqual(@as(f32, 20.0), s.data[2]); | |
| 544 | } | |
| 545 | ||
| 546 | test "use within struct scope" { | |
| 547 | const S = struct { | |
| 548 | usingnamespace struct { | |
| 549 | pub fn inner() i32 { | |
| 550 | return 42; | |
| 551 | } | |
| 552 | }; | |
| 553 | }; | |
| 554 | try expectEqual(@as(i32, 42), S.inner()); | |
| 555 | } | |
| 556 | ||
| 557 | test "default struct initialization fields" { | |
| 558 | const S = struct { | |
| 559 | a: i32 = 1234, | |
| 560 | b: i32, | |
| 561 | }; | |
| 562 | const x = S{ | |
| 563 | .b = 5, | |
| 564 | }; | |
| 565 | var five: i32 = 5; | |
| 566 | const y = S{ | |
| 567 | .b = five, | |
| 568 | }; | |
| 569 | if (x.a + x.b != 1239) { | |
| 570 | @compileError("it should be comptime known"); | |
| 571 | } | |
| 572 | try expectEqual(y, x); | |
| 573 | try expectEqual(1239, x.a + x.b); | |
| 574 | } | |
| 575 | ||
| 576 | test "fn with C calling convention returns struct by value" { | |
| 577 | const S = struct { | |
| 578 | fn entry() !void { | |
| 579 | var x = makeBar(10); | |
| 580 | try expectEqual(@as(i32, 10), x.handle); | |
| 581 | } | |
| 582 | ||
| 583 | const ExternBar = extern struct { | |
| 584 | handle: i32, | |
| 585 | }; | |
| 586 | ||
| 587 | fn makeBar(t: i32) callconv(.C) ExternBar { | |
| 588 | return ExternBar{ | |
| 589 | .handle = t, | |
| 590 | }; | |
| 591 | } | |
| 592 | }; | |
| 593 | try S.entry(); | |
| 594 | comptime try S.entry(); | |
| 595 | } | |
| 596 | ||
| 597 | test "for loop over pointers to struct, getting field from struct pointer" { | |
| 598 | const S = struct { | |
| 599 | const Foo = struct { | |
| 600 | name: []const u8, | |
| 601 | }; | |
| 602 | ||
| 603 | var ok = true; | |
| 604 | ||
| 605 | fn eql(a: []const u8) bool { | |
| 606 | _ = a; | |
| 607 | return true; | |
| 608 | } | |
| 609 | ||
| 610 | const ArrayList = struct { | |
| 611 | fn toSlice(self: *ArrayList) []*Foo { | |
| 612 | _ = self; | |
| 613 | return @as([*]*Foo, undefined)[0..0]; | |
| 614 | } | |
| 615 | }; | |
| 616 | ||
| 617 | fn doTheTest() !void { | |
| 618 | var objects: ArrayList = undefined; | |
| 619 | ||
| 620 | for (objects.toSlice()) |obj| { | |
| 621 | if (eql(obj.name)) { | |
| 622 | ok = false; | |
| 623 | } | |
| 624 | } | |
| 625 | ||
| 626 | try expect(ok); | |
| 627 | } | |
| 628 | }; | |
| 629 | try S.doTheTest(); | |
| 630 | } | |
| 631 | ||
| 632 | test "zero-bit field in packed struct" { | |
| 633 | const S = packed struct { | |
| 634 | x: u10, | |
| 635 | y: void, | |
| 636 | }; | |
| 637 | var x: S = undefined; | |
| 638 | _ = x; | |
| 639 | } | |
| 640 | ||
| 641 | test "struct field init with catch" { | |
| 642 | const S = struct { | |
| 643 | fn doTheTest() !void { | |
| 644 | var x: anyerror!isize = 1; | |
| 645 | var req = Foo{ | |
| 646 | .field = x catch undefined, | |
| 647 | }; | |
| 648 | try expect(req.field == 1); | |
| 649 | } | |
| 650 | ||
| 651 | pub const Foo = extern struct { | |
| 652 | field: isize, | |
| 653 | }; | |
| 654 | }; | |
| 655 | try S.doTheTest(); | |
| 656 | comptime try S.doTheTest(); | |
| 657 | } | |
| 658 | ||
| 659 | test "packed struct with non-ABI-aligned field" { | |
| 660 | const S = packed struct { | |
| 661 | x: u9, | |
| 662 | y: u183, | |
| 663 | }; | |
| 664 | var s: S = undefined; | |
| 665 | s.x = 1; | |
| 666 | s.y = 42; | |
| 667 | try expect(s.x == 1); | |
| 668 | try expect(s.y == 42); | |
| 669 | } | |
| 670 | ||
| 671 | test "non-packed struct with u128 entry in union" { | |
| 672 | const U = union(enum) { | |
| 673 | Num: u128, | |
| 674 | Void, | |
| 675 | }; | |
| 676 | ||
| 677 | const S = struct { | |
| 678 | f1: U, | |
| 679 | f2: U, | |
| 680 | }; | |
| 681 | ||
| 682 | var sx: S = undefined; | |
| 683 | var s = &sx; | |
| 684 | try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); | |
| 685 | var v2 = U{ .Num = 123 }; | |
| 686 | s.f2 = v2; | |
| 687 | try std.testing.expect(s.f2.Num == 123); | |
| 688 | } | |
| 689 | ||
| 690 | test "packed struct field passed to generic function" { | |
| 691 | const S = struct { | |
| 692 | const P = packed struct { | |
| 693 | b: u5, | |
| 694 | g: u5, | |
| 695 | r: u5, | |
| 696 | a: u1, | |
| 697 | }; | |
| 698 | ||
| 699 | fn genericReadPackedField(ptr: anytype) u5 { | |
| 700 | return ptr.*; | |
| 701 | } | |
| 702 | }; | |
| 703 | ||
| 704 | var p: S.P = undefined; | |
| 705 | p.b = 29; | |
| 706 | var loaded = S.genericReadPackedField(&p.b); | |
| 707 | try expect(loaded == 29); | |
| 708 | } | |
| 709 | ||
| 710 | test "anonymous struct literal syntax" { | |
| 711 | const S = struct { | |
| 712 | const Point = struct { | |
| 713 | x: i32, | |
| 714 | y: i32, | |
| 715 | }; | |
| 716 | ||
| 717 | fn doTheTest() !void { | |
| 718 | var p: Point = .{ | |
| 719 | .x = 1, | |
| 720 | .y = 2, | |
| 721 | }; | |
| 722 | try expect(p.x == 1); | |
| 723 | try expect(p.y == 2); | |
| 724 | } | |
| 725 | }; | |
| 726 | try S.doTheTest(); | |
| 727 | comptime try S.doTheTest(); | |
| 728 | } | |
| 729 | ||
| 730 | test "fully anonymous struct" { | |
| 731 | const S = struct { | |
| 732 | fn doTheTest() !void { | |
| 733 | try dump(.{ | |
| 734 | .int = @as(u32, 1234), | |
| 735 | .float = @as(f64, 12.34), | |
| 736 | .b = true, | |
| 737 | .s = "hi", | |
| 738 | }); | |
| 739 | } | |
| 740 | fn dump(args: anytype) !void { | |
| 741 | try expect(args.int == 1234); | |
| 742 | try expect(args.float == 12.34); | |
| 743 | try expect(args.b); | |
| 744 | try expect(args.s[0] == 'h'); | |
| 745 | try expect(args.s[1] == 'i'); | |
| 746 | } | |
| 747 | }; | |
| 748 | try S.doTheTest(); | |
| 749 | comptime try S.doTheTest(); | |
| 750 | } | |
| 751 | ||
| 752 | test "fully anonymous list literal" { | |
| 753 | const S = struct { | |
| 754 | fn doTheTest() !void { | |
| 755 | try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); | |
| 756 | } | |
| 757 | fn dump(args: anytype) !void { | |
| 758 | try expect(args.@"0" == 1234); | |
| 759 | try expect(args.@"1" == 12.34); | |
| 760 | try expect(args.@"2"); | |
| 761 | try expect(args.@"3"[0] == 'h'); | |
| 762 | try expect(args.@"3"[1] == 'i'); | |
| 763 | } | |
| 764 | }; | |
| 765 | try S.doTheTest(); | |
| 766 | comptime try S.doTheTest(); | |
| 767 | } | |
| 768 | ||
| 769 | test "anonymous struct literal assigned to variable" { | |
| 770 | var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; | |
| 771 | try expect(vec.@"0" == 22); | |
| 772 | try expect(vec.@"1" == 55); | |
| 773 | try expect(vec.@"2" == 99); | |
| 774 | } | |
| 775 | ||
| 776 | test "struct with var field" { | |
| 777 | const Point = struct { | |
| 778 | x: anytype, | |
| 779 | y: anytype, | |
| 780 | }; | |
| 781 | const pt = Point{ | |
| 782 | .x = 1, | |
| 783 | .y = 2, | |
| 784 | }; | |
| 785 | try expect(pt.x == 1); | |
| 786 | try expect(pt.y == 2); | |
| 787 | } | |
| 788 | ||
| 789 | test "comptime struct field" { | |
| 790 | const T = struct { | |
| 791 | a: i32, | |
| 792 | comptime b: i32 = 1234, | |
| 793 | }; | |
| 794 | ||
| 795 | var foo: T = undefined; | |
| 796 | comptime try expect(foo.b == 1234); | |
| 797 | } | |
| 798 | ||
| 799 | test "anon struct literal field value initialized with fn call" { | |
| 800 | const S = struct { | |
| 801 | fn doTheTest() !void { | |
| 802 | var x = .{foo()}; | |
| 803 | try expectEqualSlices(u8, x[0], "hi"); | |
| 804 | } | |
| 805 | fn foo() []const u8 { | |
| 806 | return "hi"; | |
| 807 | } | |
| 808 | }; | |
| 809 | try S.doTheTest(); | |
| 810 | comptime try S.doTheTest(); | |
| 811 | } | |
| 812 | ||
| 813 | test "self-referencing struct via array member" { | |
| 814 | const T = struct { | |
| 815 | children: [1]*@This(), | |
| 816 | }; | |
| 817 | var x: T = undefined; | |
| 818 | x = T{ .children = .{&x} }; | |
| 819 | try expect(x.children[0] == &x); | |
| 820 | } | |
| 821 | ||
| 822 | test "struct with union field" { | |
| 823 | const Value = struct { | |
| 824 | ref: u32 = 2, | |
| 825 | kind: union(enum) { | |
| 826 | None: usize, | |
| 827 | Bool: bool, | |
| 828 | }, | |
| 829 | }; | |
| 830 | ||
| 831 | var True = Value{ | |
| 832 | .kind = .{ .Bool = true }, | |
| 833 | }; | |
| 834 | try expectEqual(@as(u32, 2), True.ref); | |
| 835 | try expectEqual(true, True.kind.Bool); | |
| 836 | } | |
| 837 | ||
| 838 | test "type coercion of anon struct literal to struct" { | |
| 839 | const S = struct { | |
| 840 | const S2 = struct { | |
| 841 | A: u32, | |
| 842 | B: []const u8, | |
| 843 | C: void, | |
| 844 | D: Foo = .{}, | |
| 845 | }; | |
| 846 | ||
| 847 | const Foo = struct { | |
| 848 | field: i32 = 1234, | |
| 849 | }; | |
| 850 | ||
| 851 | fn doTheTest() !void { | |
| 852 | var y: u32 = 42; | |
| 853 | const t0 = .{ .A = 123, .B = "foo", .C = {} }; | |
| 854 | const t1 = .{ .A = y, .B = "foo", .C = {} }; | |
| 855 | const y0: S2 = t0; | |
| 856 | var y1: S2 = t1; | |
| 857 | try expect(y0.A == 123); | |
| 858 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 859 | try expect(y0.C == {}); | |
| 860 | try expect(y0.D.field == 1234); | |
| 861 | try expect(y1.A == y); | |
| 862 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 863 | try expect(y1.C == {}); | |
| 864 | try expect(y1.D.field == 1234); | |
| 865 | } | |
| 866 | }; | |
| 867 | try S.doTheTest(); | |
| 868 | comptime try S.doTheTest(); | |
| 869 | } | |
| 870 | ||
| 871 | test "type coercion of pointer to anon struct literal to pointer to struct" { | |
| 872 | const S = struct { | |
| 873 | const S2 = struct { | |
| 874 | A: u32, | |
| 875 | B: []const u8, | |
| 876 | C: void, | |
| 877 | D: Foo = .{}, | |
| 878 | }; | |
| 879 | ||
| 880 | const Foo = struct { | |
| 881 | field: i32 = 1234, | |
| 882 | }; | |
| 883 | ||
| 884 | fn doTheTest() !void { | |
| 885 | var y: u32 = 42; | |
| 886 | const t0 = &.{ .A = 123, .B = "foo", .C = {} }; | |
| 887 | const t1 = &.{ .A = y, .B = "foo", .C = {} }; | |
| 888 | const y0: *const S2 = t0; | |
| 889 | var y1: *const S2 = t1; | |
| 890 | try expect(y0.A == 123); | |
| 891 | try expect(std.mem.eql(u8, y0.B, "foo")); | |
| 892 | try expect(y0.C == {}); | |
| 893 | try expect(y0.D.field == 1234); | |
| 894 | try expect(y1.A == y); | |
| 895 | try expect(std.mem.eql(u8, y1.B, "foo")); | |
| 896 | try expect(y1.C == {}); | |
| 897 | try expect(y1.D.field == 1234); | |
| 898 | } | |
| 899 | }; | |
| 900 | try S.doTheTest(); | |
| 901 | comptime try S.doTheTest(); | |
| 902 | } | |
| 903 | ||
| 904 | test "packed struct with undefined initializers" { | |
| 905 | const S = struct { | |
| 906 | const P = packed struct { | |
| 907 | a: u3, | |
| 908 | _a: u3 = undefined, | |
| 909 | b: u3, | |
| 910 | _b: u3 = undefined, | |
| 911 | c: u3, | |
| 912 | _c: u3 = undefined, | |
| 913 | }; | |
| 914 | ||
| 915 | fn doTheTest() !void { | |
| 916 | var p: P = undefined; | |
| 917 | p = P{ .a = 2, .b = 4, .c = 6 }; | |
| 918 | // Make sure the compiler doesn't touch the unprefixed fields. | |
| 919 | // Use expect since i386-linux doesn't like expectEqual | |
| 920 | try expect(p.a == 2); | |
| 921 | try expect(p.b == 4); | |
| 922 | try expect(p.c == 6); | |
| 923 | } | |
| 924 | }; | |
| 925 | ||
| 926 | try S.doTheTest(); | |
| 927 | comptime try S.doTheTest(); | |
| 928 | } |