authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-28 20:48:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-28 23:22:09-07:00
log91619cdf57f54accbdbb3ff616856eaf79b537a3
tree9da2362e728f068103a1ab83a89411f825a5c10b
parent2dd725571302a2e6e4101565554e88b4a10b0994

Sema: implement calling a fn ptr via a union field

Also, ignore `packed` on unions because that will be removed from the language.

4 files changed, 48 insertions(+), 57 deletions(-)

src/Module.zig-3
......@@ -1104,9 +1104,6 @@ pub const Union = struct {
11041104
11051105 pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
11061106 assert(u.status == .have_layout);
1107 const is_packed = u.layout == .Packed;
1108 if (is_packed) @panic("TODO packed unions");
1109
11101107 var most_aligned_field: usize = undefined;
11111108 var most_aligned_field_size: u64 = undefined;
11121109 var biggest_field: usize = undefined;
src/Sema.zig+36-22
......@@ -12152,34 +12152,16 @@ fn fieldCallBind(
1215212152 const field_index = @intCast(u32, field_index_usize);
1215312153 const field = struct_obj.fields.values()[field_index];
1215412154
12155 const ptr_field_ty = try Type.ptr(arena, .{
12156 .pointee_type = field.ty,
12157 .mutable = ptr_ty.ptrIsMutable(),
12158 .@"addrspace" = ptr_ty.ptrAddressSpace(),
12159 });
12160
12161 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
12162 const pointer = try sema.addConstant(
12163 ptr_field_ty,
12164 try Value.Tag.field_ptr.create(arena, .{
12165 .container_ptr = struct_ptr_val,
12166 .field_index = field_index,
12167 }),
12168 );
12169 return sema.analyzeLoad(block, src, pointer, src);
12170 }
12171
12172 try sema.requireRuntimeBlock(block, src);
12173 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
12174 return sema.analyzeLoad(block, src, ptr_inst, src);
12155 return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr);
1217512156 },
1217612157 .Union => {
1217712158 const union_ty = try sema.resolveTypeFields(block, src, concrete_ty);
1217812159 const fields = union_ty.unionFields();
1217912160 const field_index_usize = fields.getIndex(field_name) orelse break :find_field;
12161 const field_index = @intCast(u32, field_index_usize);
12162 const field = fields.values()[field_index];
1218012163
12181 _ = field_index_usize;
12182 return sema.fail(block, src, "TODO implement field calls on unions", .{});
12164 return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr);
1218312165 },
1218412166 .Type => {
1218512167 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
......@@ -12236,6 +12218,38 @@ fn fieldCallBind(
1223612218 return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name });
1223712219}
1223812220
12221fn finishFieldCallBind(
12222 sema: *Sema,
12223 block: *Block,
12224 src: LazySrcLoc,
12225 ptr_ty: Type,
12226 field_ty: Type,
12227 field_index: u32,
12228 object_ptr: Air.Inst.Ref,
12229) CompileError!Air.Inst.Ref {
12230 const arena = sema.arena;
12231 const ptr_field_ty = try Type.ptr(arena, .{
12232 .pointee_type = field_ty,
12233 .mutable = ptr_ty.ptrIsMutable(),
12234 .@"addrspace" = ptr_ty.ptrAddressSpace(),
12235 });
12236
12237 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
12238 const pointer = try sema.addConstant(
12239 ptr_field_ty,
12240 try Value.Tag.field_ptr.create(arena, .{
12241 .container_ptr = struct_ptr_val,
12242 .field_index = field_index,
12243 }),
12244 );
12245 return sema.analyzeLoad(block, src, pointer, src);
12246 }
12247
12248 try sema.requireRuntimeBlock(block, src);
12249 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
12250 return sema.analyzeLoad(block, src, ptr_inst, src);
12251}
12252
1223912253fn namespaceLookup(
1224012254 sema: *Sema,
1224112255 block: *Block,
test/behavior/union.zig+12
......@@ -165,6 +165,18 @@ test "union with specified enum tag" {
165165 comptime try doTest();
166166}
167167
168test "packed union generates correctly aligned LLVM type" {
169 const U = packed union {
170 f1: fn () error{TestUnexpectedResult}!void,
171 f2: u32,
172 };
173 var foo = [_]U{
174 U{ .f1 = doTest },
175 U{ .f2 = 0 },
176 };
177 try foo[0].f1();
178}
179
168180fn doTest() error{TestUnexpectedResult}!void {
169181 try expect((try bar(Payload{ .A = 1234 })) == -10);
170182}
test/behavior/union_stage1.zig-32
......@@ -3,38 +3,6 @@ const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const Tag = std.meta.Tag;
55
6const Letter = enum { A, B, C };
7const Payload = union(Letter) {
8 A: i32,
9 B: f64,
10 C: bool,
11};
12
13fn doTest() error{TestUnexpectedResult}!void {
14 try expect((try bar(Payload{ .A = 1234 })) == -10);
15}
16
17fn bar(value: Payload) error{TestUnexpectedResult}!i32 {
18 try expect(@as(Letter, value) == Letter.A);
19 return switch (value) {
20 Payload.A => |x| return x - 1244,
21 Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21,
22 Payload.C => |x| if (x) @as(i32, 30) else 31,
23 };
24}
25
26test "packed union generates correctly aligned LLVM type" {
27 const U = packed union {
28 f1: fn () error{TestUnexpectedResult}!void,
29 f2: u32,
30 };
31 var foo = [_]U{
32 U{ .f1 = doTest },
33 U{ .f2 = 0 },
34 };
35 try foo[0].f1();
36}
37
386const MultipleChoice = union(enum(u32)) {
397 A = 20,
408 B = 40,