Commit 736d14fd5f

Andrew Kelley <andrew@ziglang.org>
2021-09-23 06:02:24
stage2: fix AstGen for some struct syntaxes
* 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.
1 parent 0ec01e5
Changed files (6)
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,
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,
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,
         };
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 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,
-        };
+const empty_global_instance = StructWithNoFields{};
 
-        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 "return empty struct instance" {
+    _ = returnEmptyStructInstance();
 }
-
-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;
 }
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();
+}
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");