Commit 91619cdf57

Andrew Kelley <andrew@ziglang.org>
2021-12-29 04:48:21
Sema: implement calling a fn ptr via a union field
Also, ignore `packed` on unions because that will be removed from the language.
1 parent 2dd7255
Changed files (4)
src/Module.zig
@@ -1104,9 +1104,6 @@ pub const Union = struct {
 
     pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
         assert(u.status == .have_layout);
-        const is_packed = u.layout == .Packed;
-        if (is_packed) @panic("TODO packed unions");
-
         var most_aligned_field: usize = undefined;
         var most_aligned_field_size: u64 = undefined;
         var biggest_field: usize = undefined;
src/Sema.zig
@@ -12152,34 +12152,16 @@ fn fieldCallBind(
                 const field_index = @intCast(u32, field_index_usize);
                 const field = struct_obj.fields.values()[field_index];
 
-                const ptr_field_ty = try Type.ptr(arena, .{
-                    .pointee_type = field.ty,
-                    .mutable = ptr_ty.ptrIsMutable(),
-                    .@"addrspace" = ptr_ty.ptrAddressSpace(),
-                });
-
-                if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
-                    const pointer = try sema.addConstant(
-                        ptr_field_ty,
-                        try Value.Tag.field_ptr.create(arena, .{
-                            .container_ptr = struct_ptr_val,
-                            .field_index = field_index,
-                        }),
-                    );
-                    return sema.analyzeLoad(block, src, pointer, src);
-                }
-
-                try sema.requireRuntimeBlock(block, src);
-                const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
-                return sema.analyzeLoad(block, src, ptr_inst, src);
+                return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr);
             },
             .Union => {
                 const union_ty = try sema.resolveTypeFields(block, src, concrete_ty);
                 const fields = union_ty.unionFields();
                 const field_index_usize = fields.getIndex(field_name) orelse break :find_field;
+                const field_index = @intCast(u32, field_index_usize);
+                const field = fields.values()[field_index];
 
-                _ = field_index_usize;
-                return sema.fail(block, src, "TODO implement field calls on unions", .{});
+                return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr);
             },
             .Type => {
                 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
@@ -12236,6 +12218,38 @@ fn fieldCallBind(
     return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name });
 }
 
+fn finishFieldCallBind(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    ptr_ty: Type,
+    field_ty: Type,
+    field_index: u32,
+    object_ptr: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
+    const arena = sema.arena;
+    const ptr_field_ty = try Type.ptr(arena, .{
+        .pointee_type = field_ty,
+        .mutable = ptr_ty.ptrIsMutable(),
+        .@"addrspace" = ptr_ty.ptrAddressSpace(),
+    });
+
+    if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
+        const pointer = try sema.addConstant(
+            ptr_field_ty,
+            try Value.Tag.field_ptr.create(arena, .{
+                .container_ptr = struct_ptr_val,
+                .field_index = field_index,
+            }),
+        );
+        return sema.analyzeLoad(block, src, pointer, src);
+    }
+
+    try sema.requireRuntimeBlock(block, src);
+    const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
+    return sema.analyzeLoad(block, src, ptr_inst, src);
+}
+
 fn namespaceLookup(
     sema: *Sema,
     block: *Block,
test/behavior/union.zig
@@ -165,6 +165,18 @@ test "union with specified enum tag" {
     comptime try doTest();
 }
 
+test "packed union generates correctly aligned LLVM type" {
+    const U = packed union {
+        f1: fn () error{TestUnexpectedResult}!void,
+        f2: u32,
+    };
+    var foo = [_]U{
+        U{ .f1 = doTest },
+        U{ .f2 = 0 },
+    };
+    try foo[0].f1();
+}
+
 fn doTest() error{TestUnexpectedResult}!void {
     try expect((try bar(Payload{ .A = 1234 })) == -10);
 }
test/behavior/union_stage1.zig
@@ -3,38 +3,6 @@ const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
 const Tag = std.meta.Tag;
 
-const Letter = enum { A, B, C };
-const Payload = union(Letter) {
-    A: i32,
-    B: f64,
-    C: bool,
-};
-
-fn doTest() error{TestUnexpectedResult}!void {
-    try expect((try bar(Payload{ .A = 1234 })) == -10);
-}
-
-fn bar(value: Payload) error{TestUnexpectedResult}!i32 {
-    try expect(@as(Letter, value) == Letter.A);
-    return switch (value) {
-        Payload.A => |x| return x - 1244,
-        Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21,
-        Payload.C => |x| if (x) @as(i32, 30) else 31,
-    };
-}
-
-test "packed union generates correctly aligned LLVM type" {
-    const U = packed union {
-        f1: fn () error{TestUnexpectedResult}!void,
-        f2: u32,
-    };
-    var foo = [_]U{
-        U{ .f1 = doTest },
-        U{ .f2 = 0 },
-    };
-    try foo[0].f1();
-}
-
 const MultipleChoice = union(enum(u32)) {
     A = 20,
     B = 40,