| 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,7 +1357,14 @@ fn structInitExpr( |
| 1357 | const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) { | 1357 | const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) { |
| 1358 | .array_type => tree.arrayType(struct_init.ast.type_expr), | 1358 | .array_type => tree.arrayType(struct_init.ast.type_expr), |
| 1359 | .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr), | 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 | const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and | 1369 | const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and |
| 1363 | // This intentionally does not support `@"_"` syntax. | 1370 | // This intentionally does not support `@"_"` syntax. |
| ... | @@ -1419,8 +1426,8 @@ fn structInitExpr( | ... | @@ -1419,8 +1426,8 @@ fn structInitExpr( |
| 1419 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); | 1426 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); |
| 1420 | return rvalue(gz, rl, result, node); | 1427 | return rvalue(gz, rl, result, node); |
| 1421 | }, | 1428 | }, |
| 1422 | .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst), | 1429 | .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst), |
| 1423 | .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr), | 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,6 +1466,26 @@ fn structInitExprRlNone( |
| 1459 | } | 1466 | } |
| 1460 | 1467 | ||
| 1461 | fn structInitExprRlPtr( | 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 | gz: *GenZir, | 1489 | gz: *GenZir, |
| 1463 | scope: *Scope, | 1490 | scope: *Scope, |
| 1464 | node: Ast.Node.Index, | 1491 | node: Ast.Node.Index, |
| ... | @@ -1472,9 +1499,6 @@ fn structInitExprRlPtr( | ... | @@ -1472,9 +1499,6 @@ fn structInitExprRlPtr( |
| 1472 | const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); | 1499 | const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); |
| 1473 | defer gpa.free(field_ptr_list); | 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 | for (struct_init.ast.fields) |field_init, i| { | 1502 | for (struct_init.ast.fields) |field_init, i| { |
| 1479 | const name_token = tree.firstToken(field_init) - 2; | 1503 | const name_token = tree.firstToken(field_init) - 2; |
| 1480 | const str_index = try astgen.identAsString(name_token); | 1504 | const str_index = try astgen.identAsString(name_token); |
| ... | @@ -1489,7 +1513,7 @@ fn structInitExprRlPtr( | ... | @@ -1489,7 +1513,7 @@ fn structInitExprRlPtr( |
| 1489 | .body_len = @intCast(u32, field_ptr_list.len), | 1513 | .body_len = @intCast(u32, field_ptr_list.len), |
| 1490 | }); | 1514 | }); |
| 1491 | try astgen.extra.appendSlice(gpa, field_ptr_list); | 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 | fn structInitExprRlTy( | 1519 | fn structInitExprRlTy( |
| ... | @@ -6902,35 +6926,13 @@ fn asRlPtr( | ... | @@ -6902,35 +6926,13 @@ fn asRlPtr( |
| 6902 | operand_node: Ast.Node.Index, | 6926 | operand_node: Ast.Node.Index, |
| 6903 | dest_type: Zir.Inst.Ref, | 6927 | dest_type: Zir.Inst.Ref, |
| 6904 | ) InnerError!Zir.Inst.Ref { | 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 | const astgen = parent_gz.astgen; | 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 | defer as_scope.instructions.deinit(astgen.gpa); | 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 | const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node); | 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; | 6935 | return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type); |
| 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 | } | ||
| 6934 | } | 6936 | } |
| 6935 | 6937 | ||
| 6936 | fn bitCast( | 6938 | fn bitCast( |
| ... | @@ -9108,6 +9110,52 @@ const GenZir = struct { | ... | @@ -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 | const Label = struct { | 9159 | const Label = struct { |
| 9112 | token: Ast.TokenIndex, | 9160 | token: Ast.TokenIndex, |
| 9113 | block_inst: Zir.Inst.Index, | 9161 | block_inst: Zir.Inst.Index, |
src/Zir.zig+6-2| ... | @@ -2650,8 +2650,12 @@ pub const Inst = struct { | ... | @@ -2650,8 +2650,12 @@ pub const Inst = struct { |
| 2650 | has_decls_len: bool, | 2650 | has_decls_len: bool, |
| 2651 | name_strategy: NameStrategy, | 2651 | name_strategy: NameStrategy, |
| 2652 | layout: std.builtin.TypeInfo.ContainerLayout, | 2652 | layout: std.builtin.TypeInfo.ContainerLayout, |
| 2653 | /// false: union(tag_type) | 2653 | /// has_tag_type | auto_enum_tag | result |
| 2654 | /// true: union(enum(tag_type)) | 2654 | /// ------------------------------------- |
| 2655 | /// false | false | union { } | ||
| 2656 | /// false | true | union(enum) { } | ||
| 2657 | /// true | true | union(enum(T)) { } | ||
| 2658 | /// true | false | union(T) { } | ||
| 2655 | auto_enum_tag: bool, | 2659 | auto_enum_tag: bool, |
| 2656 | _: u6 = undefined, | 2660 | _: u6 = undefined, |
| 2657 | }; | 2661 | }; |
src/codegen/llvm.zig+6-1| ... | @@ -563,8 +563,13 @@ pub const DeclGen = struct { | ... | @@ -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 | const fn_type = llvm.functionType( | 571 | const fn_type = llvm.functionType( |
| 567 | try self.llvmType(return_type), | 572 | llvm_ret_ty, |
| 568 | llvm_param_buffer.ptr, | 573 | llvm_param_buffer.ptr, |
| 569 | llvm_params_len, | 574 | llvm_params_len, |
| 570 | .False, | 575 | .False, |
test/behavior.zig+2-1| ... | @@ -13,6 +13,7 @@ test { | ... | @@ -13,6 +13,7 @@ test { |
| 13 | _ = @import("behavior/atomics.zig"); | 13 | _ = @import("behavior/atomics.zig"); |
| 14 | _ = @import("behavior/sizeof_and_typeof.zig"); | 14 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| 15 | _ = @import("behavior/translate_c_macros.zig"); | 15 | _ = @import("behavior/translate_c_macros.zig"); |
| 16 | _ = @import("behavior/struct.zig"); | ||
| 16 | _ = @import("behavior/union.zig"); | 17 | _ = @import("behavior/union.zig"); |
| 17 | _ = @import("behavior/widening.zig"); | 18 | _ = @import("behavior/widening.zig"); |
| 18 | 19 | ||
| ... | @@ -135,7 +136,7 @@ test { | ... | @@ -135,7 +136,7 @@ test { |
| 135 | _ = @import("behavior/sizeof_and_typeof_stage1.zig"); | 136 | _ = @import("behavior/sizeof_and_typeof_stage1.zig"); |
| 136 | _ = @import("behavior/slice.zig"); | 137 | _ = @import("behavior/slice.zig"); |
| 137 | _ = @import("behavior/slice_sentinel_comptime.zig"); | 138 | _ = @import("behavior/slice_sentinel_comptime.zig"); |
| 138 | _ = @import("behavior/struct.zig"); | 139 | _ = @import("behavior/struct_stage1.zig"); |
| 139 | _ = @import("behavior/struct_contains_null_ptr_itself.zig"); | 140 | _ = @import("behavior/struct_contains_null_ptr_itself.zig"); |
| 140 | _ = @import("behavior/struct_contains_slice_of_itself.zig"); | 141 | _ = @import("behavior/struct_contains_slice_of_itself.zig"); |
| 141 | _ = @import("behavior/switch.zig"); | 142 | _ = @import("behavior/switch.zig"); |
test/behavior/struct.zig+6-926| ... | @@ -5,949 +5,29 @@ const expect = std.testing.expect; | ... | @@ -5,949 +5,29 @@ const expect = std.testing.expect; |
| 5 | const expectEqual = std.testing.expectEqual; | 5 | const expectEqual = std.testing.expectEqual; |
| 6 | const expectEqualSlices = std.testing.expectEqualSlices; | 6 | const expectEqualSlices = std.testing.expectEqualSlices; |
| 7 | const maxInt = std.math.maxInt; | 7 | const maxInt = std.math.maxInt; |
| 8 | |||
| 8 | const StructWithNoFields = struct { | 9 | const StructWithNoFields = struct { |
| 9 | fn add(a: i32, b: i32) i32 { | 10 | fn add(a: i32, b: i32) i32 { |
| 10 | return a + b; | 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 | test "call struct static method" { | 15 | test "call struct static method" { |
| 26 | const result = StructWithNoFields.add(3, 4); | 16 | const result = StructWithNoFields.add(3, 4); |
| 27 | try expect(result == 7); | 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 | const should_be_11 = StructWithNoFields.add(5, 6); | 20 | const should_be_11 = StructWithNoFields.add(5, 6); |
| 38 | 21 | ||
| 39 | test "invoke static method in global scope" { | 22 | test "invoke static method in global scope" { |
| 40 | try expect(should_be_11 == 11); | 23 | try expect(should_be_11 == 11); |
| 41 | } | 24 | } |
| 42 | 25 | ||
| 43 | test "void struct fields" { | 26 | const empty_global_instance = StructWithNoFields{}; |
| 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 | }; | ||
| 908 | 27 | ||
| 909 | fn doTheTest() !void { | 28 | test "return empty struct instance" { |
| 910 | var y: u32 = 42; | 29 | _ = returnEmptyStructInstance(); |
| 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(); | ||
| 927 | } | 30 | } |
| 928 | 31 | fn returnEmptyStructInstance() StructWithNoFields { | |
| 929 | test "packed struct with undefined initializers" { | 32 | return empty_global_instance; |
| 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(); | ||
| 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 | } | ||