diff --git a/src/AstGen.zig b/src/AstGen.zig index 6a533dff11..b33c5aad40 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1357,7 +1357,14 @@ fn structInitExpr( const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) { .array_type => tree.arrayType(struct_init.ast.type_expr), .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr), - else => break :array, + else => { + if (struct_init.ast.fields.len == 0) { + const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); + const result = try gz.addUnNode(.struct_init_empty, ty_inst, node); + return rvalue(gz, rl, result, node); + } + break :array; + }, }; const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and // This intentionally does not support `@"_"` syntax. @@ -1419,8 +1426,8 @@ fn structInitExpr( const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); return rvalue(gz, rl, result, node); }, - .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst), - .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, node, struct_init, block_gz.rl_ptr), + .ptr, .inferred_ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst), + .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr), } } @@ -1459,6 +1466,26 @@ fn structInitExprRlNone( } fn structInitExprRlPtr( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: Ast.Node.Index, + struct_init: Ast.full.StructInit, + result_ptr: Zir.Inst.Ref, +) InnerError!Zir.Inst.Ref { + if (struct_init.ast.type_expr == 0) { + return structInitExprRlPtrInner(gz, scope, node, struct_init, result_ptr); + } + const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); + + var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr); + defer as_scope.instructions.deinit(gz.astgen.gpa); + + const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr); + return as_scope.finishCoercion(gz, rl, node, result, ty_inst); +} + +fn structInitExprRlPtrInner( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, @@ -1472,9 +1499,6 @@ fn structInitExprRlPtr( const field_ptr_list = try gpa.alloc(Zir.Inst.Index, struct_init.ast.fields.len); defer gpa.free(field_ptr_list); - if (struct_init.ast.type_expr != 0) - _ = try typeExpr(gz, scope, struct_init.ast.type_expr); - for (struct_init.ast.fields) |field_init, i| { const name_token = tree.firstToken(field_init) - 2; const str_index = try astgen.identAsString(name_token); @@ -1489,7 +1513,7 @@ fn structInitExprRlPtr( .body_len = @intCast(u32, field_ptr_list.len), }); try astgen.extra.appendSlice(gpa, field_ptr_list); - return .void_value; + return Zir.Inst.Ref.void_value; } fn structInitExprRlTy( @@ -6902,35 +6926,13 @@ fn asRlPtr( operand_node: Ast.Node.Index, dest_type: Zir.Inst.Ref, ) InnerError!Zir.Inst.Ref { - // Detect whether this expr() call goes into rvalue() to store the result into the - // result location. If it does, elide the coerce_result_ptr instruction - // as well as the store instruction, instead passing the result as an rvalue. const astgen = parent_gz.astgen; - var as_scope = parent_gz.makeSubBlock(scope); + var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr); defer as_scope.instructions.deinit(astgen.gpa); - as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node); - const parent_zir = &parent_gz.instructions; - if (as_scope.rvalue_rl_count == 1) { - // Busted! This expression didn't actually need a pointer. - const zir_tags = astgen.instructions.items(.tag); - const zir_datas = astgen.instructions.items(.data); - try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); - for (as_scope.instructions.items) |src_inst| { - if (indexToRef(src_inst) == as_scope.rl_ptr) continue; - if (zir_tags[src_inst] == .store_to_block_ptr) { - if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; - } - parent_zir.appendAssumeCapacity(src_inst); - } - const casted_result = try parent_gz.addBin(.as, dest_type, result); - return rvalue(parent_gz, rl, casted_result, operand_node); - } else { - try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items); - return result; - } + return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type); } fn bitCast( @@ -9108,6 +9110,52 @@ const GenZir = struct { }; } + fn makeCoercionScope( + parent_gz: *GenZir, + scope: *Scope, + dest_type: Zir.Inst.Ref, + result_ptr: Zir.Inst.Ref, + ) !GenZir { + // Detect whether this expr() call goes into rvalue() to store the result into the + // result location. If it does, elide the coerce_result_ptr instruction + // as well as the store instruction, instead passing the result as an rvalue. + var as_scope = parent_gz.makeSubBlock(scope); + errdefer as_scope.instructions.deinit(parent_gz.astgen.gpa); + as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); + + return as_scope; + } + + fn finishCoercion( + as_scope: *GenZir, + parent_gz: *GenZir, + rl: ResultLoc, + src_node: Ast.Node.Index, + result: Zir.Inst.Ref, + dest_type: Zir.Inst.Ref, + ) !Zir.Inst.Ref { + const astgen = as_scope.astgen; + const parent_zir = &parent_gz.instructions; + if (as_scope.rvalue_rl_count == 1) { + // Busted! This expression didn't actually need a pointer. + const zir_tags = astgen.instructions.items(.tag); + const zir_datas = astgen.instructions.items(.data); + try parent_zir.ensureUnusedCapacity(astgen.gpa, as_scope.instructions.items.len); + for (as_scope.instructions.items) |src_inst| { + if (indexToRef(src_inst) == as_scope.rl_ptr) continue; + if (zir_tags[src_inst] == .store_to_block_ptr) { + if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; + } + parent_zir.appendAssumeCapacity(src_inst); + } + const casted_result = try parent_gz.addBin(.as, dest_type, result); + return rvalue(parent_gz, rl, casted_result, src_node); + } else { + try parent_zir.appendSlice(astgen.gpa, as_scope.instructions.items); + return result; + } + } + const Label = struct { token: Ast.TokenIndex, block_inst: Zir.Inst.Index, diff --git a/src/Zir.zig b/src/Zir.zig index d3b1678d97..7a07f0aaaa 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2650,8 +2650,12 @@ pub const Inst = struct { has_decls_len: bool, name_strategy: NameStrategy, layout: std.builtin.TypeInfo.ContainerLayout, - /// false: union(tag_type) - /// true: union(enum(tag_type)) + /// has_tag_type | auto_enum_tag | result + /// ------------------------------------- + /// false | false | union { } + /// false | true | union(enum) { } + /// true | true | union(enum(T)) { } + /// true | false | union(T) { } auto_enum_tag: bool, _: u6 = undefined, }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0d3bca2b03..6daf720961 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -563,8 +563,13 @@ pub const DeclGen = struct { } } + const llvm_ret_ty = if (!return_type.hasCodeGenBits()) + self.context.voidType() + else + try self.llvmType(return_type); + const fn_type = llvm.functionType( - try self.llvmType(return_type), + llvm_ret_ty, llvm_param_buffer.ptr, llvm_params_len, .False, diff --git a/test/behavior.zig b/test/behavior.zig index 7fbba99a8c..78f3651b01 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,6 +13,7 @@ test { _ = @import("behavior/atomics.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); _ = @import("behavior/translate_c_macros.zig"); + _ = @import("behavior/struct.zig"); _ = @import("behavior/union.zig"); _ = @import("behavior/widening.zig"); @@ -135,7 +136,7 @@ test { _ = @import("behavior/sizeof_and_typeof_stage1.zig"); _ = @import("behavior/slice.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); - _ = @import("behavior/struct.zig"); + _ = @import("behavior/struct_stage1.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch.zig"); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index d9e1c02aa1..6f00b71057 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -5,949 +5,29 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const expectEqualSlices = std.testing.expectEqualSlices; const maxInt = std.math.maxInt; + const StructWithNoFields = struct { fn add(a: i32, b: i32) i32 { return a + b; } }; -const empty_global_instance = StructWithNoFields{}; - -top_level_field: i32, - -test "top level fields" { - var instance = @This(){ - .top_level_field = 1234, - }; - instance.top_level_field += 1; - try expectEqual(@as(i32, 1235), instance.top_level_field); -} test "call struct static method" { const result = StructWithNoFields.add(3, 4); try expect(result == 7); } -test "return empty struct instance" { - _ = returnEmptyStructInstance(); -} -fn returnEmptyStructInstance() StructWithNoFields { - return empty_global_instance; -} - const should_be_11 = StructWithNoFields.add(5, 6); test "invoke static method in global scope" { try expect(should_be_11 == 11); } -test "void struct fields" { - const foo = VoidStructFieldsFoo{ - .a = void{}, - .b = 1, - .c = void{}, - }; - try expect(foo.b == 1); - try expect(@sizeOf(VoidStructFieldsFoo) == 4); +const empty_global_instance = StructWithNoFields{}; + +test "return empty struct instance" { + _ = returnEmptyStructInstance(); } -const VoidStructFieldsFoo = struct { - a: void, - b: i32, - c: void, -}; - -test "structs" { - var foo: StructFoo = undefined; - @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); - foo.a += 1; - foo.b = foo.a == 1; - try testFoo(foo); - testMutation(&foo); - try expect(foo.c == 100); -} -const StructFoo = struct { - a: i32, - b: bool, - c: f32, -}; -fn testFoo(foo: StructFoo) !void { - try expect(foo.b); -} -fn testMutation(foo: *StructFoo) void { - foo.c = 100; -} - -const Node = struct { - val: Val, - next: *Node, -}; - -const Val = struct { - x: i32, -}; - -test "struct point to self" { - var root: Node = undefined; - root.val.x = 1; - - var node: Node = undefined; - node.next = &root; - node.val.x = 2; - - root.next = &node; - - try expect(node.next.next.next.val.x == 1); -} - -test "struct byval assign" { - var foo1: StructFoo = undefined; - var foo2: StructFoo = undefined; - - foo1.a = 1234; - foo2.a = 0; - try expect(foo2.a == 0); - foo2 = foo1; - try expect(foo2.a == 1234); -} - -fn structInitializer() void { - const val = Val{ .x = 42 }; - try expect(val.x == 42); -} - -test "fn call of struct field" { - const Foo = struct { - ptr: fn () i32, - }; - const S = struct { - fn aFunc() i32 { - return 13; - } - - fn callStructField(foo: Foo) i32 { - return foo.ptr(); - } - }; - - try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); -} - -test "store member function in variable" { - const instance = MemberFnTestFoo{ .x = 1234 }; - const memberFn = MemberFnTestFoo.member; - const result = memberFn(instance); - try expect(result == 1234); -} -const MemberFnTestFoo = struct { - x: i32, - fn member(foo: MemberFnTestFoo) i32 { - return foo.x; - } -}; - -test "call member function directly" { - const instance = MemberFnTestFoo{ .x = 1234 }; - const result = MemberFnTestFoo.member(instance); - try expect(result == 1234); -} - -test "member functions" { - const r = MemberFnRand{ .seed = 1234 }; - try expect(r.getSeed() == 1234); -} -const MemberFnRand = struct { - seed: u32, - pub fn getSeed(r: *const MemberFnRand) u32 { - return r.seed; - } -}; - -test "return struct byval from function" { - const bar = makeBar2(1234, 5678); - try expect(bar.y == 5678); -} -const Bar = struct { - x: i32, - y: i32, -}; -fn makeBar2(x: i32, y: i32) Bar { - return Bar{ - .x = x, - .y = y, - }; -} - -test "empty struct method call" { - const es = EmptyStruct{}; - try expect(es.method() == 1234); -} -const EmptyStruct = struct { - fn method(es: *const EmptyStruct) i32 { - _ = es; - return 1234; - } -}; - -test "return empty struct from fn" { - _ = testReturnEmptyStructFromFn(); -} -const EmptyStruct2 = struct {}; -fn testReturnEmptyStructFromFn() EmptyStruct2 { - return EmptyStruct2{}; -} - -test "pass slice of empty struct to fn" { - try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); -} -fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { - return slice.len; -} - -const APackedStruct = packed struct { - x: u8, - y: u8, -}; - -test "packed struct" { - var foo = APackedStruct{ - .x = 1, - .y = 2, - }; - foo.y += 1; - const four = foo.x + foo.y; - try expect(four == 4); -} - -const BitField1 = packed struct { - a: u3, - b: u3, - c: u2, -}; - -const bit_field_1 = BitField1{ - .a = 1, - .b = 2, - .c = 3, -}; - -test "bit field access" { - var data = bit_field_1; - try expect(getA(&data) == 1); - try expect(getB(&data) == 2); - try expect(getC(&data) == 3); - comptime try expect(@sizeOf(BitField1) == 1); - - data.b += 1; - try expect(data.b == 3); - - data.a += 1; - try expect(data.a == 2); - try expect(data.b == 3); -} - -fn getA(data: *const BitField1) u3 { - return data.a; -} - -fn getB(data: *const BitField1) u3 { - return data.b; -} - -fn getC(data: *const BitField1) u2 { - return data.c; -} - -const Foo24Bits = packed struct { - field: u24, -}; -const Foo96Bits = packed struct { - a: u24, - b: u24, - c: u24, - d: u24, -}; - -test "packed struct 24bits" { - comptime { - try expect(@sizeOf(Foo24Bits) == 4); - if (@sizeOf(usize) == 4) { - try expect(@sizeOf(Foo96Bits) == 12); - } else { - try expect(@sizeOf(Foo96Bits) == 16); - } - } - - var value = Foo96Bits{ - .a = 0, - .b = 0, - .c = 0, - .d = 0, - }; - value.a += 1; - try expect(value.a == 1); - try expect(value.b == 0); - try expect(value.c == 0); - try expect(value.d == 0); - - value.b += 1; - try expect(value.a == 1); - try expect(value.b == 1); - try expect(value.c == 0); - try expect(value.d == 0); - - value.c += 1; - try expect(value.a == 1); - try expect(value.b == 1); - try expect(value.c == 1); - try expect(value.d == 0); - - value.d += 1; - try expect(value.a == 1); - try expect(value.b == 1); - try expect(value.c == 1); - try expect(value.d == 1); -} - -const Foo32Bits = packed struct { - field: u24, - pad: u8, -}; - -const FooArray24Bits = packed struct { - a: u16, - b: [2]Foo32Bits, - c: u16, -}; - -// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 -test "packed array 24bits" { - comptime { - try expect(@sizeOf([9]Foo32Bits) == 9 * 4); - try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); - } - - var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); - bytes[bytes.len - 1] = 0xaa; - const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - try expect(ptr.a == 0); - try expect(ptr.b[0].field == 0); - try expect(ptr.b[1].field == 0); - try expect(ptr.c == 0); - - ptr.a = maxInt(u16); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == 0); - try expect(ptr.b[1].field == 0); - try expect(ptr.c == 0); - - ptr.b[0].field = maxInt(u24); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == maxInt(u24)); - try expect(ptr.b[1].field == 0); - try expect(ptr.c == 0); - - ptr.b[1].field = maxInt(u24); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == maxInt(u24)); - try expect(ptr.b[1].field == maxInt(u24)); - try expect(ptr.c == 0); - - ptr.c = maxInt(u16); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == maxInt(u24)); - try expect(ptr.b[1].field == maxInt(u24)); - try expect(ptr.c == maxInt(u16)); - - try expect(bytes[bytes.len - 1] == 0xaa); -} - -const FooStructAligned = packed struct { - a: u8, - b: u8, -}; - -const FooArrayOfAligned = packed struct { - a: [2]FooStructAligned, -}; - -test "aligned array of packed struct" { - comptime { - try expect(@sizeOf(FooStructAligned) == 2); - try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); - } - - var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); - const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; - - try expect(ptr.a[0].a == 0xbb); - try expect(ptr.a[0].b == 0xbb); - try expect(ptr.a[1].a == 0xbb); - try expect(ptr.a[1].b == 0xbb); -} - -test "runtime struct initialization of bitfield" { - const s1 = Nibbles{ - .x = x1, - .y = x1, - }; - const s2 = Nibbles{ - .x = @intCast(u4, x2), - .y = @intCast(u4, x2), - }; - - try expect(s1.x == x1); - try expect(s1.y == x1); - try expect(s2.x == @intCast(u4, x2)); - try expect(s2.y == @intCast(u4, x2)); -} - -var x1 = @as(u4, 1); -var x2 = @as(u8, 2); - -const Nibbles = packed struct { - x: u4, - y: u4, -}; - -const Bitfields = packed struct { - f1: u16, - f2: u16, - f3: u8, - f4: u8, - f5: u4, - f6: u4, - f7: u8, -}; - -test "native bit field understands endianness" { - var all: u64 = if (native_endian != .Little) - 0x1111222233445677 - else - 0x7765443322221111; - var bytes: [8]u8 = undefined; - @memcpy(&bytes, @ptrCast([*]u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, &bytes).*; - - try expect(bitfields.f1 == 0x1111); - try expect(bitfields.f2 == 0x2222); - try expect(bitfields.f3 == 0x33); - try expect(bitfields.f4 == 0x44); - try expect(bitfields.f5 == 0x5); - try expect(bitfields.f6 == 0x6); - try expect(bitfields.f7 == 0x77); -} - -test "align 1 field before self referential align 8 field as slice return type" { - const result = alloc(Expr); - try expect(result.len == 0); -} - -const Expr = union(enum) { - Literal: u8, - Question: *Expr, -}; - -fn alloc(comptime T: type) []T { - return &[_]T{}; -} - -test "call method with mutable reference to struct with no fields" { - const S = struct { - fn doC(s: *const @This()) bool { - _ = s; - return true; - } - fn do(s: *@This()) bool { - _ = s; - return true; - } - }; - - var s = S{}; - try expect(S.doC(&s)); - try expect(s.doC()); - try expect(S.do(&s)); - try expect(s.do()); -} - -test "implicit cast packed struct field to const ptr" { - const LevelUpMove = packed struct { - move_id: u9, - level: u7, - - fn toInt(value: u7) u7 { - return value; - } - }; - - var lup: LevelUpMove = undefined; - lup.level = 12; - const res = LevelUpMove.toInt(lup.level); - try expect(res == 12); -} - -test "pointer to packed struct member in a stack variable" { - const S = packed struct { - a: u2, - b: u2, - }; - - var s = S{ .a = 2, .b = 0 }; - var b_ptr = &s.b; - try expect(s.b == 0); - b_ptr.* = 2; - try expect(s.b == 2); -} - -test "non-byte-aligned array inside packed struct" { - const Foo = packed struct { - a: bool, - b: [0x16]u8, - }; - const S = struct { - fn bar(slice: []const u8) !void { - try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); - } - fn doTheTest() !void { - var foo = Foo{ - .a = true, - .b = "abcdefghijklmnopqurstu".*, - }; - const value = foo.b; - try bar(&value); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "packed struct with u0 field access" { - const S = packed struct { - f0: u0, - }; - var s = S{ .f0 = 0 }; - comptime try expect(s.f0 == 0); -} - -const S0 = struct { - bar: S1, - - pub const S1 = struct { - value: u8, - }; - - fn init() @This() { - return S0{ .bar = S1{ .value = 123 } }; - } -}; - -var g_foo: S0 = S0.init(); - -test "access to global struct fields" { - g_foo.bar.value = 42; - try expect(g_foo.bar.value == 42); -} - -test "packed struct with fp fields" { - const S = packed struct { - data: [3]f32, - - pub fn frob(self: *@This()) void { - self.data[0] += self.data[1] + self.data[2]; - self.data[1] += self.data[0] + self.data[2]; - self.data[2] += self.data[0] + self.data[1]; - } - }; - - var s: S = undefined; - s.data[0] = 1.0; - s.data[1] = 2.0; - s.data[2] = 3.0; - s.frob(); - try expectEqual(@as(f32, 6.0), s.data[0]); - try expectEqual(@as(f32, 11.0), s.data[1]); - try expectEqual(@as(f32, 20.0), s.data[2]); -} - -test "use within struct scope" { - const S = struct { - usingnamespace struct { - pub fn inner() i32 { - return 42; - } - }; - }; - try expectEqual(@as(i32, 42), S.inner()); -} - -test "default struct initialization fields" { - const S = struct { - a: i32 = 1234, - b: i32, - }; - const x = S{ - .b = 5, - }; - var five: i32 = 5; - const y = S{ - .b = five, - }; - if (x.a + x.b != 1239) { - @compileError("it should be comptime known"); - } - try expectEqual(y, x); - try expectEqual(1239, x.a + x.b); -} - -test "fn with C calling convention returns struct by value" { - const S = struct { - fn entry() !void { - var x = makeBar(10); - try expectEqual(@as(i32, 10), x.handle); - } - - const ExternBar = extern struct { - handle: i32, - }; - - fn makeBar(t: i32) callconv(.C) ExternBar { - return ExternBar{ - .handle = t, - }; - } - }; - try S.entry(); - comptime try S.entry(); -} - -test "for loop over pointers to struct, getting field from struct pointer" { - const S = struct { - const Foo = struct { - name: []const u8, - }; - - var ok = true; - - fn eql(a: []const u8) bool { - _ = a; - return true; - } - - const ArrayList = struct { - fn toSlice(self: *ArrayList) []*Foo { - _ = self; - return @as([*]*Foo, undefined)[0..0]; - } - }; - - fn doTheTest() !void { - var objects: ArrayList = undefined; - - for (objects.toSlice()) |obj| { - if (eql(obj.name)) { - ok = false; - } - } - - try expect(ok); - } - }; - try S.doTheTest(); -} - -test "zero-bit field in packed struct" { - const S = packed struct { - x: u10, - y: void, - }; - var x: S = undefined; - _ = x; -} - -test "struct field init with catch" { - const S = struct { - fn doTheTest() !void { - var x: anyerror!isize = 1; - var req = Foo{ - .field = x catch undefined, - }; - try expect(req.field == 1); - } - - pub const Foo = extern struct { - field: isize, - }; - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "packed struct with non-ABI-aligned field" { - const S = packed struct { - x: u9, - y: u183, - }; - var s: S = undefined; - s.x = 1; - s.y = 42; - try expect(s.x == 1); - try expect(s.y == 42); -} - -test "non-packed struct with u128 entry in union" { - const U = union(enum) { - Num: u128, - Void, - }; - - const S = struct { - f1: U, - f2: U, - }; - - var sx: S = undefined; - var s = &sx; - try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); - var v2 = U{ .Num = 123 }; - s.f2 = v2; - try std.testing.expect(s.f2.Num == 123); -} - -test "packed struct field passed to generic function" { - const S = struct { - const P = packed struct { - b: u5, - g: u5, - r: u5, - a: u1, - }; - - fn genericReadPackedField(ptr: anytype) u5 { - return ptr.*; - } - }; - - var p: S.P = undefined; - p.b = 29; - var loaded = S.genericReadPackedField(&p.b); - try expect(loaded == 29); -} - -test "anonymous struct literal syntax" { - const S = struct { - const Point = struct { - x: i32, - y: i32, - }; - - fn doTheTest() !void { - var p: Point = .{ - .x = 1, - .y = 2, - }; - try expect(p.x == 1); - try expect(p.y == 2); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "fully anonymous struct" { - const S = struct { - fn doTheTest() !void { - try dump(.{ - .int = @as(u32, 1234), - .float = @as(f64, 12.34), - .b = true, - .s = "hi", - }); - } - fn dump(args: anytype) !void { - try expect(args.int == 1234); - try expect(args.float == 12.34); - try expect(args.b); - try expect(args.s[0] == 'h'); - try expect(args.s[1] == 'i'); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "fully anonymous list literal" { - const S = struct { - fn doTheTest() !void { - try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); - } - fn dump(args: anytype) !void { - try expect(args.@"0" == 1234); - try expect(args.@"1" == 12.34); - try expect(args.@"2"); - try expect(args.@"3"[0] == 'h'); - try expect(args.@"3"[1] == 'i'); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "anonymous struct literal assigned to variable" { - var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; - try expect(vec.@"0" == 22); - try expect(vec.@"1" == 55); - try expect(vec.@"2" == 99); -} - -test "struct with var field" { - const Point = struct { - x: anytype, - y: anytype, - }; - const pt = Point{ - .x = 1, - .y = 2, - }; - try expect(pt.x == 1); - try expect(pt.y == 2); -} - -test "comptime struct field" { - const T = struct { - a: i32, - comptime b: i32 = 1234, - }; - - var foo: T = undefined; - comptime try expect(foo.b == 1234); -} - -test "anon struct literal field value initialized with fn call" { - const S = struct { - fn doTheTest() !void { - var x = .{foo()}; - try expectEqualSlices(u8, x[0], "hi"); - } - fn foo() []const u8 { - return "hi"; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "self-referencing struct via array member" { - const T = struct { - children: [1]*@This(), - }; - var x: T = undefined; - x = T{ .children = .{&x} }; - try expect(x.children[0] == &x); -} - -test "struct with union field" { - const Value = struct { - ref: u32 = 2, - kind: union(enum) { - None: usize, - Bool: bool, - }, - }; - - var True = Value{ - .kind = .{ .Bool = true }, - }; - try expectEqual(@as(u32, 2), True.ref); - try expectEqual(true, True.kind.Bool); -} - -test "type coercion of anon struct literal to struct" { - const S = struct { - const S2 = struct { - A: u32, - B: []const u8, - C: void, - D: Foo = .{}, - }; - - const Foo = struct { - field: i32 = 1234, - }; - - fn doTheTest() !void { - var y: u32 = 42; - const t0 = .{ .A = 123, .B = "foo", .C = {} }; - const t1 = .{ .A = y, .B = "foo", .C = {} }; - const y0: S2 = t0; - var y1: S2 = t1; - try expect(y0.A == 123); - try expect(std.mem.eql(u8, y0.B, "foo")); - try expect(y0.C == {}); - try expect(y0.D.field == 1234); - try expect(y1.A == y); - try expect(std.mem.eql(u8, y1.B, "foo")); - try expect(y1.C == {}); - try expect(y1.D.field == 1234); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type coercion of pointer to anon struct literal to pointer to struct" { - const S = struct { - const S2 = struct { - A: u32, - B: []const u8, - C: void, - D: Foo = .{}, - }; - - const Foo = struct { - field: i32 = 1234, - }; - - fn doTheTest() !void { - var y: u32 = 42; - const t0 = &.{ .A = 123, .B = "foo", .C = {} }; - const t1 = &.{ .A = y, .B = "foo", .C = {} }; - const y0: *const S2 = t0; - var y1: *const S2 = t1; - try expect(y0.A == 123); - try expect(std.mem.eql(u8, y0.B, "foo")); - try expect(y0.C == {}); - try expect(y0.D.field == 1234); - try expect(y1.A == y); - try expect(std.mem.eql(u8, y1.B, "foo")); - try expect(y1.C == {}); - try expect(y1.D.field == 1234); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "packed struct with undefined initializers" { - const S = struct { - const P = packed struct { - a: u3, - _a: u3 = undefined, - b: u3, - _b: u3 = undefined, - c: u3, - _c: u3 = undefined, - }; - - fn doTheTest() !void { - var p: P = undefined; - p = P{ .a = 2, .b = 4, .c = 6 }; - // Make sure the compiler doesn't touch the unprefixed fields. - // Use expect since i386-linux doesn't like expectEqual - try expect(p.a == 2); - try expect(p.b == 4); - try expect(p.c == 6); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); +fn returnEmptyStructInstance() StructWithNoFields { + return empty_global_instance; } diff --git a/test/behavior/struct_stage1.zig b/test/behavior/struct_stage1.zig new file mode 100644 index 0000000000..9f084ceb85 --- /dev/null +++ b/test/behavior/struct_stage1.zig @@ -0,0 +1,928 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const native_endian = builtin.target.cpu.arch.endian(); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const expectEqualSlices = std.testing.expectEqualSlices; +const maxInt = std.math.maxInt; +top_level_field: i32, + +test "top level fields" { + var instance = @This(){ + .top_level_field = 1234, + }; + instance.top_level_field += 1; + try expectEqual(@as(i32, 1235), instance.top_level_field); +} + +test "void struct fields" { + const foo = VoidStructFieldsFoo{ + .a = void{}, + .b = 1, + .c = void{}, + }; + try expect(foo.b == 1); + try expect(@sizeOf(VoidStructFieldsFoo) == 4); +} +const VoidStructFieldsFoo = struct { + a: void, + b: i32, + c: void, +}; + +test "structs" { + var foo: StructFoo = undefined; + @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); + foo.a += 1; + foo.b = foo.a == 1; + try testFoo(foo); + testMutation(&foo); + try expect(foo.c == 100); +} +const StructFoo = struct { + a: i32, + b: bool, + c: f32, +}; +fn testFoo(foo: StructFoo) !void { + try expect(foo.b); +} +fn testMutation(foo: *StructFoo) void { + foo.c = 100; +} + +const Node = struct { + val: Val, + next: *Node, +}; + +const Val = struct { + x: i32, +}; + +test "struct point to self" { + var root: Node = undefined; + root.val.x = 1; + + var node: Node = undefined; + node.next = &root; + node.val.x = 2; + + root.next = &node; + + try expect(node.next.next.next.val.x == 1); +} + +test "struct byval assign" { + var foo1: StructFoo = undefined; + var foo2: StructFoo = undefined; + + foo1.a = 1234; + foo2.a = 0; + try expect(foo2.a == 0); + foo2 = foo1; + try expect(foo2.a == 1234); +} + +fn structInitializer() void { + const val = Val{ .x = 42 }; + try expect(val.x == 42); +} + +test "fn call of struct field" { + const Foo = struct { + ptr: fn () i32, + }; + const S = struct { + fn aFunc() i32 { + return 13; + } + + fn callStructField(foo: Foo) i32 { + return foo.ptr(); + } + }; + + try expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); +} + +test "store member function in variable" { + const instance = MemberFnTestFoo{ .x = 1234 }; + const memberFn = MemberFnTestFoo.member; + const result = memberFn(instance); + try expect(result == 1234); +} +const MemberFnTestFoo = struct { + x: i32, + fn member(foo: MemberFnTestFoo) i32 { + return foo.x; + } +}; + +test "call member function directly" { + const instance = MemberFnTestFoo{ .x = 1234 }; + const result = MemberFnTestFoo.member(instance); + try expect(result == 1234); +} + +test "member functions" { + const r = MemberFnRand{ .seed = 1234 }; + try expect(r.getSeed() == 1234); +} +const MemberFnRand = struct { + seed: u32, + pub fn getSeed(r: *const MemberFnRand) u32 { + return r.seed; + } +}; + +test "return struct byval from function" { + const bar = makeBar2(1234, 5678); + try expect(bar.y == 5678); +} +const Bar = struct { + x: i32, + y: i32, +}; +fn makeBar2(x: i32, y: i32) Bar { + return Bar{ + .x = x, + .y = y, + }; +} + +test "empty struct method call" { + const es = EmptyStruct{}; + try expect(es.method() == 1234); +} +const EmptyStruct = struct { + fn method(es: *const EmptyStruct) i32 { + _ = es; + return 1234; + } +}; + +test "return empty struct from fn" { + _ = testReturnEmptyStructFromFn(); +} +const EmptyStruct2 = struct {}; +fn testReturnEmptyStructFromFn() EmptyStruct2 { + return EmptyStruct2{}; +} + +test "pass slice of empty struct to fn" { + try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); +} +fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { + return slice.len; +} + +const APackedStruct = packed struct { + x: u8, + y: u8, +}; + +test "packed struct" { + var foo = APackedStruct{ + .x = 1, + .y = 2, + }; + foo.y += 1; + const four = foo.x + foo.y; + try expect(four == 4); +} + +const BitField1 = packed struct { + a: u3, + b: u3, + c: u2, +}; + +const bit_field_1 = BitField1{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "bit field access" { + var data = bit_field_1; + try expect(getA(&data) == 1); + try expect(getB(&data) == 2); + try expect(getC(&data) == 3); + comptime try expect(@sizeOf(BitField1) == 1); + + data.b += 1; + try expect(data.b == 3); + + data.a += 1; + try expect(data.a == 2); + try expect(data.b == 3); +} + +fn getA(data: *const BitField1) u3 { + return data.a; +} + +fn getB(data: *const BitField1) u3 { + return data.b; +} + +fn getC(data: *const BitField1) u2 { + return data.c; +} + +const Foo24Bits = packed struct { + field: u24, +}; +const Foo96Bits = packed struct { + a: u24, + b: u24, + c: u24, + d: u24, +}; + +test "packed struct 24bits" { + comptime { + try expect(@sizeOf(Foo24Bits) == 4); + if (@sizeOf(usize) == 4) { + try expect(@sizeOf(Foo96Bits) == 12); + } else { + try expect(@sizeOf(Foo96Bits) == 16); + } + } + + var value = Foo96Bits{ + .a = 0, + .b = 0, + .c = 0, + .d = 0, + }; + value.a += 1; + try expect(value.a == 1); + try expect(value.b == 0); + try expect(value.c == 0); + try expect(value.d == 0); + + value.b += 1; + try expect(value.a == 1); + try expect(value.b == 1); + try expect(value.c == 0); + try expect(value.d == 0); + + value.c += 1; + try expect(value.a == 1); + try expect(value.b == 1); + try expect(value.c == 1); + try expect(value.d == 0); + + value.d += 1; + try expect(value.a == 1); + try expect(value.b == 1); + try expect(value.c == 1); + try expect(value.d == 1); +} + +const Foo32Bits = packed struct { + field: u24, + pad: u8, +}; + +const FooArray24Bits = packed struct { + a: u16, + b: [2]Foo32Bits, + c: u16, +}; + +// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 +test "packed array 24bits" { + comptime { + try expect(@sizeOf([9]Foo32Bits) == 9 * 4); + try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); + } + + var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); + bytes[bytes.len - 1] = 0xaa; + const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; + try expect(ptr.a == 0); + try expect(ptr.b[0].field == 0); + try expect(ptr.b[1].field == 0); + try expect(ptr.c == 0); + + ptr.a = maxInt(u16); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == 0); + try expect(ptr.b[1].field == 0); + try expect(ptr.c == 0); + + ptr.b[0].field = maxInt(u24); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == maxInt(u24)); + try expect(ptr.b[1].field == 0); + try expect(ptr.c == 0); + + ptr.b[1].field = maxInt(u24); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == maxInt(u24)); + try expect(ptr.b[1].field == maxInt(u24)); + try expect(ptr.c == 0); + + ptr.c = maxInt(u16); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == maxInt(u24)); + try expect(ptr.b[1].field == maxInt(u24)); + try expect(ptr.c == maxInt(u16)); + + try expect(bytes[bytes.len - 1] == 0xaa); +} + +const FooStructAligned = packed struct { + a: u8, + b: u8, +}; + +const FooArrayOfAligned = packed struct { + a: [2]FooStructAligned, +}; + +test "aligned array of packed struct" { + comptime { + try expect(@sizeOf(FooStructAligned) == 2); + try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); + } + + var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); + const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; + + try expect(ptr.a[0].a == 0xbb); + try expect(ptr.a[0].b == 0xbb); + try expect(ptr.a[1].a == 0xbb); + try expect(ptr.a[1].b == 0xbb); +} + +test "runtime struct initialization of bitfield" { + const s1 = Nibbles{ + .x = x1, + .y = x1, + }; + const s2 = Nibbles{ + .x = @intCast(u4, x2), + .y = @intCast(u4, x2), + }; + + try expect(s1.x == x1); + try expect(s1.y == x1); + try expect(s2.x == @intCast(u4, x2)); + try expect(s2.y == @intCast(u4, x2)); +} + +var x1 = @as(u4, 1); +var x2 = @as(u8, 2); + +const Nibbles = packed struct { + x: u4, + y: u4, +}; + +const Bitfields = packed struct { + f1: u16, + f2: u16, + f3: u8, + f4: u8, + f5: u4, + f6: u4, + f7: u8, +}; + +test "native bit field understands endianness" { + var all: u64 = if (native_endian != .Little) + 0x1111222233445677 + else + 0x7765443322221111; + var bytes: [8]u8 = undefined; + @memcpy(&bytes, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, &bytes).*; + + try expect(bitfields.f1 == 0x1111); + try expect(bitfields.f2 == 0x2222); + try expect(bitfields.f3 == 0x33); + try expect(bitfields.f4 == 0x44); + try expect(bitfields.f5 == 0x5); + try expect(bitfields.f6 == 0x6); + try expect(bitfields.f7 == 0x77); +} + +test "align 1 field before self referential align 8 field as slice return type" { + const result = alloc(Expr); + try expect(result.len == 0); +} + +const Expr = union(enum) { + Literal: u8, + Question: *Expr, +}; + +fn alloc(comptime T: type) []T { + return &[_]T{}; +} + +test "call method with mutable reference to struct with no fields" { + const S = struct { + fn doC(s: *const @This()) bool { + _ = s; + return true; + } + fn do(s: *@This()) bool { + _ = s; + return true; + } + }; + + var s = S{}; + try expect(S.doC(&s)); + try expect(s.doC()); + try expect(S.do(&s)); + try expect(s.do()); +} + +test "implicit cast packed struct field to const ptr" { + const LevelUpMove = packed struct { + move_id: u9, + level: u7, + + fn toInt(value: u7) u7 { + return value; + } + }; + + var lup: LevelUpMove = undefined; + lup.level = 12; + const res = LevelUpMove.toInt(lup.level); + try expect(res == 12); +} + +test "pointer to packed struct member in a stack variable" { + const S = packed struct { + a: u2, + b: u2, + }; + + var s = S{ .a = 2, .b = 0 }; + var b_ptr = &s.b; + try expect(s.b == 0); + b_ptr.* = 2; + try expect(s.b == 2); +} + +test "non-byte-aligned array inside packed struct" { + const Foo = packed struct { + a: bool, + b: [0x16]u8, + }; + const S = struct { + fn bar(slice: []const u8) !void { + try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); + } + fn doTheTest() !void { + var foo = Foo{ + .a = true, + .b = "abcdefghijklmnopqurstu".*, + }; + const value = foo.b; + try bar(&value); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "packed struct with u0 field access" { + const S = packed struct { + f0: u0, + }; + var s = S{ .f0 = 0 }; + comptime try expect(s.f0 == 0); +} + +const S0 = struct { + bar: S1, + + pub const S1 = struct { + value: u8, + }; + + fn init() @This() { + return S0{ .bar = S1{ .value = 123 } }; + } +}; + +var g_foo: S0 = S0.init(); + +test "access to global struct fields" { + g_foo.bar.value = 42; + try expect(g_foo.bar.value == 42); +} + +test "packed struct with fp fields" { + const S = packed struct { + data: [3]f32, + + pub fn frob(self: *@This()) void { + self.data[0] += self.data[1] + self.data[2]; + self.data[1] += self.data[0] + self.data[2]; + self.data[2] += self.data[0] + self.data[1]; + } + }; + + var s: S = undefined; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.frob(); + try expectEqual(@as(f32, 6.0), s.data[0]); + try expectEqual(@as(f32, 11.0), s.data[1]); + try expectEqual(@as(f32, 20.0), s.data[2]); +} + +test "use within struct scope" { + const S = struct { + usingnamespace struct { + pub fn inner() i32 { + return 42; + } + }; + }; + try expectEqual(@as(i32, 42), S.inner()); +} + +test "default struct initialization fields" { + const S = struct { + a: i32 = 1234, + b: i32, + }; + const x = S{ + .b = 5, + }; + var five: i32 = 5; + const y = S{ + .b = five, + }; + if (x.a + x.b != 1239) { + @compileError("it should be comptime known"); + } + try expectEqual(y, x); + try expectEqual(1239, x.a + x.b); +} + +test "fn with C calling convention returns struct by value" { + const S = struct { + fn entry() !void { + var x = makeBar(10); + try expectEqual(@as(i32, 10), x.handle); + } + + const ExternBar = extern struct { + handle: i32, + }; + + fn makeBar(t: i32) callconv(.C) ExternBar { + return ExternBar{ + .handle = t, + }; + } + }; + try S.entry(); + comptime try S.entry(); +} + +test "for loop over pointers to struct, getting field from struct pointer" { + const S = struct { + const Foo = struct { + name: []const u8, + }; + + var ok = true; + + fn eql(a: []const u8) bool { + _ = a; + return true; + } + + const ArrayList = struct { + fn toSlice(self: *ArrayList) []*Foo { + _ = self; + return @as([*]*Foo, undefined)[0..0]; + } + }; + + fn doTheTest() !void { + var objects: ArrayList = undefined; + + for (objects.toSlice()) |obj| { + if (eql(obj.name)) { + ok = false; + } + } + + try expect(ok); + } + }; + try S.doTheTest(); +} + +test "zero-bit field in packed struct" { + const S = packed struct { + x: u10, + y: void, + }; + var x: S = undefined; + _ = x; +} + +test "struct field init with catch" { + const S = struct { + fn doTheTest() !void { + var x: anyerror!isize = 1; + var req = Foo{ + .field = x catch undefined, + }; + try expect(req.field == 1); + } + + pub const Foo = extern struct { + field: isize, + }; + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "packed struct with non-ABI-aligned field" { + const S = packed struct { + x: u9, + y: u183, + }; + var s: S = undefined; + s.x = 1; + s.y = 42; + try expect(s.x == 1); + try expect(s.y == 42); +} + +test "non-packed struct with u128 entry in union" { + const U = union(enum) { + Num: u128, + Void, + }; + + const S = struct { + f1: U, + f2: U, + }; + + var sx: S = undefined; + var s = &sx; + try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); + var v2 = U{ .Num = 123 }; + s.f2 = v2; + try std.testing.expect(s.f2.Num == 123); +} + +test "packed struct field passed to generic function" { + const S = struct { + const P = packed struct { + b: u5, + g: u5, + r: u5, + a: u1, + }; + + fn genericReadPackedField(ptr: anytype) u5 { + return ptr.*; + } + }; + + var p: S.P = undefined; + p.b = 29; + var loaded = S.genericReadPackedField(&p.b); + try expect(loaded == 29); +} + +test "anonymous struct literal syntax" { + const S = struct { + const Point = struct { + x: i32, + y: i32, + }; + + fn doTheTest() !void { + var p: Point = .{ + .x = 1, + .y = 2, + }; + try expect(p.x == 1); + try expect(p.y == 2); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "fully anonymous struct" { + const S = struct { + fn doTheTest() !void { + try dump(.{ + .int = @as(u32, 1234), + .float = @as(f64, 12.34), + .b = true, + .s = "hi", + }); + } + fn dump(args: anytype) !void { + try expect(args.int == 1234); + try expect(args.float == 12.34); + try expect(args.b); + try expect(args.s[0] == 'h'); + try expect(args.s[1] == 'i'); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "fully anonymous list literal" { + const S = struct { + fn doTheTest() !void { + try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); + } + fn dump(args: anytype) !void { + try expect(args.@"0" == 1234); + try expect(args.@"1" == 12.34); + try expect(args.@"2"); + try expect(args.@"3"[0] == 'h'); + try expect(args.@"3"[1] == 'i'); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "anonymous struct literal assigned to variable" { + var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; + try expect(vec.@"0" == 22); + try expect(vec.@"1" == 55); + try expect(vec.@"2" == 99); +} + +test "struct with var field" { + const Point = struct { + x: anytype, + y: anytype, + }; + const pt = Point{ + .x = 1, + .y = 2, + }; + try expect(pt.x == 1); + try expect(pt.y == 2); +} + +test "comptime struct field" { + const T = struct { + a: i32, + comptime b: i32 = 1234, + }; + + var foo: T = undefined; + comptime try expect(foo.b == 1234); +} + +test "anon struct literal field value initialized with fn call" { + const S = struct { + fn doTheTest() !void { + var x = .{foo()}; + try expectEqualSlices(u8, x[0], "hi"); + } + fn foo() []const u8 { + return "hi"; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "self-referencing struct via array member" { + const T = struct { + children: [1]*@This(), + }; + var x: T = undefined; + x = T{ .children = .{&x} }; + try expect(x.children[0] == &x); +} + +test "struct with union field" { + const Value = struct { + ref: u32 = 2, + kind: union(enum) { + None: usize, + Bool: bool, + }, + }; + + var True = Value{ + .kind = .{ .Bool = true }, + }; + try expectEqual(@as(u32, 2), True.ref); + try expectEqual(true, True.kind.Bool); +} + +test "type coercion of anon struct literal to struct" { + const S = struct { + const S2 = struct { + A: u32, + B: []const u8, + C: void, + D: Foo = .{}, + }; + + const Foo = struct { + field: i32 = 1234, + }; + + fn doTheTest() !void { + var y: u32 = 42; + const t0 = .{ .A = 123, .B = "foo", .C = {} }; + const t1 = .{ .A = y, .B = "foo", .C = {} }; + const y0: S2 = t0; + var y1: S2 = t1; + try expect(y0.A == 123); + try expect(std.mem.eql(u8, y0.B, "foo")); + try expect(y0.C == {}); + try expect(y0.D.field == 1234); + try expect(y1.A == y); + try expect(std.mem.eql(u8, y1.B, "foo")); + try expect(y1.C == {}); + try expect(y1.D.field == 1234); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type coercion of pointer to anon struct literal to pointer to struct" { + const S = struct { + const S2 = struct { + A: u32, + B: []const u8, + C: void, + D: Foo = .{}, + }; + + const Foo = struct { + field: i32 = 1234, + }; + + fn doTheTest() !void { + var y: u32 = 42; + const t0 = &.{ .A = 123, .B = "foo", .C = {} }; + const t1 = &.{ .A = y, .B = "foo", .C = {} }; + const y0: *const S2 = t0; + var y1: *const S2 = t1; + try expect(y0.A == 123); + try expect(std.mem.eql(u8, y0.B, "foo")); + try expect(y0.C == {}); + try expect(y0.D.field == 1234); + try expect(y1.A == y); + try expect(std.mem.eql(u8, y1.B, "foo")); + try expect(y1.C == {}); + try expect(y1.D.field == 1234); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "packed struct with undefined initializers" { + const S = struct { + const P = packed struct { + a: u3, + _a: u3 = undefined, + b: u3, + _b: u3 = undefined, + c: u3, + _c: u3 = undefined, + }; + + fn doTheTest() !void { + var p: P = undefined; + p = P{ .a = 2, .b = 4, .c = 6 }; + // Make sure the compiler doesn't touch the unprefixed fields. + // Use expect since i386-linux doesn't like expectEqual + try expect(p.a == 2); + try expect(p.b == 4); + try expect(p.c == 6); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +}