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 {...@@ -1104,9 +1104,6 @@ pub const Union = struct {
11041104
1105 pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {1105 pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
1106 assert(u.status == .have_layout);1106 assert(u.status == .have_layout);
1107 const is_packed = u.layout == .Packed;
1108 if (is_packed) @panic("TODO packed unions");
1109
1110 var most_aligned_field: usize = undefined;1107 var most_aligned_field: usize = undefined;
1111 var most_aligned_field_size: u64 = undefined;1108 var most_aligned_field_size: u64 = undefined;
1112 var biggest_field: usize = undefined;1109 var biggest_field: usize = undefined;
src/Sema.zig+36-22
...@@ -12152,34 +12152,16 @@ fn fieldCallBind(...@@ -12152,34 +12152,16 @@ fn fieldCallBind(
12152 const field_index = @intCast(u32, field_index_usize);12152 const field_index = @intCast(u32, field_index_usize);
12153 const field = struct_obj.fields.values()[field_index];12153 const field = struct_obj.fields.values()[field_index];
1215412154
12155 const ptr_field_ty = try Type.ptr(arena, .{12155 return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr);
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);
12175 },12156 },
12176 .Union => {12157 .Union => {
12177 const union_ty = try sema.resolveTypeFields(block, src, concrete_ty);12158 const union_ty = try sema.resolveTypeFields(block, src, concrete_ty);
12178 const fields = union_ty.unionFields();12159 const fields = union_ty.unionFields();
12179 const field_index_usize = fields.getIndex(field_name) orelse break :find_field;12160 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;12164 return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr);
12182 return sema.fail(block, src, "TODO implement field calls on unions", .{});
12183 },12165 },
12184 .Type => {12166 .Type => {
12185 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);12167 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
...@@ -12236,6 +12218,38 @@ fn fieldCallBind(...@@ -12236,6 +12218,38 @@ fn fieldCallBind(
12236 return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name });12218 return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name });
12237}12219}
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
12239fn namespaceLookup(12253fn namespaceLookup(
12240 sema: *Sema,12254 sema: *Sema,
12241 block: *Block,12255 block: *Block,
test/behavior/union.zig+12
...@@ -165,6 +165,18 @@ test "union with specified enum tag" {...@@ -165,6 +165,18 @@ test "union with specified enum tag" {
165 comptime try doTest();165 comptime try doTest();
166}166}
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
168fn doTest() error{TestUnexpectedResult}!void {180fn doTest() error{TestUnexpectedResult}!void {
169 try expect((try bar(Payload{ .A = 1234 })) == -10);181 try expect((try bar(Payload{ .A = 1234 })) == -10);
170}182}
test/behavior/union_stage1.zig-32
...@@ -3,38 +3,6 @@ const expect = std.testing.expect;...@@ -3,38 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;4const 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
38const MultipleChoice = union(enum(u32)) {6const MultipleChoice = union(enum(u32)) {
39 A = 20,7 A = 20,
40 B = 40,8 B = 40,