authorgravatar for emily.a.bellows@hey.comEmily Bellows <emily.a.bellows@hey.com> 2021-10-31 14:21:02-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-02 12:45:29-04:00
log674932e503e5b3a929f5a17eb4aa2fd2866219c3
tree996ee589e424fde8f61fe6edd1a02001c49001e2
parent325bae7fc0025f7153bd1de1c28e642e896190b1

C backend: implement ?void, and other zero sized types


5 files changed, 75 insertions(+), 29 deletions(-)

src/codegen/c.zig+16-1
...@@ -308,6 +308,11 @@ pub const DeclGen = struct {...@@ -308,6 +308,11 @@ pub const DeclGen = struct {
308 if (ty.isPtrLikeOptional()) {308 if (ty.isPtrLikeOptional()) {
309 return dg.renderValue(writer, payload_type, val);309 return dg.renderValue(writer, payload_type, val);
310 }310 }
311 const target = dg.module.getTarget();
312 if (payload_type.abiSize(target) == 0) {
313 const is_null = val.castTag(.opt_payload) == null;
314 return writer.print("{}", .{is_null});
315 }
311 try writer.writeByte('(');316 try writer.writeByte('(');
312 try dg.renderType(writer, ty);317 try dg.renderType(writer, ty);
313 try writer.writeAll("){");318 try writer.writeAll("){");
...@@ -588,10 +593,13 @@ pub const DeclGen = struct {...@@ -588,10 +593,13 @@ pub const DeclGen = struct {
588 .Optional => {593 .Optional => {
589 var opt_buf: Type.Payload.ElemType = undefined;594 var opt_buf: Type.Payload.ElemType = undefined;
590 const child_type = t.optionalChild(&opt_buf);595 const child_type = t.optionalChild(&opt_buf);
596 const target = dg.module.getTarget();
591 if (t.isPtrLikeOptional()) {597 if (t.isPtrLikeOptional()) {
592 return dg.renderType(w, child_type);598 return dg.renderType(w, child_type);
593 } else if (dg.typedefs.get(t)) |some| {599 } else if (dg.typedefs.get(t)) |some| {
594 return w.writeAll(some.name);600 return w.writeAll(some.name);
601 } else if (child_type.abiSize(target) == 0) {
602 return w.writeAll("bool");
595 }603 }
596604
597 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);605 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
...@@ -2100,14 +2108,21 @@ fn airIsNull(...@@ -2100,14 +2108,21 @@ fn airIsNull(
2100 const un_op = f.air.instructions.items(.data)[inst].un_op;2108 const un_op = f.air.instructions.items(.data)[inst].un_op;
2101 const writer = f.object.writer();2109 const writer = f.object.writer();
2102 const operand = try f.resolveInst(un_op);2110 const operand = try f.resolveInst(un_op);
2111 const target = f.object.dg.module.getTarget();
21032112
2104 const local = try f.allocLocal(Type.initTag(.bool), .Const);2113 const local = try f.allocLocal(Type.initTag(.bool), .Const);
2105 try writer.writeAll(" = (");2114 try writer.writeAll(" = (");
2106 try f.writeCValue(writer, operand);2115 try f.writeCValue(writer, operand);
21072116
2108 if (f.air.typeOf(un_op).isPtrLikeOptional()) {2117 const ty = f.air.typeOf(un_op);
2118 var opt_buf: Type.Payload.ElemType = undefined;
2119 const payload_type = ty.optionalChild(&opt_buf);
2120
2121 if (ty.isPtrLikeOptional()) {
2109 // operand is a regular pointer, test `operand !=/== NULL`2122 // operand is a regular pointer, test `operand !=/== NULL`
2110 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });2123 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
2124 } else if (payload_type.abiSize(target) == 0) {
2125 try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
2111 } else {2126 } else {
2112 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });2127 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
2113 }2128 }
src/type.zig+7-1
...@@ -1805,7 +1805,13 @@ pub const Type = extern union {...@@ -1805,7 +1805,13 @@ pub const Type = extern union {
1805 .void,1805 .void,
1806 => 0,1806 => 0,
18071807
1808 .@"struct" => return self.structFieldOffset(self.structFieldCount(), target),1808 .@"struct" => {
1809 const field_count = self.structFieldCount();
1810 if (field_count == 0) {
1811 return 0;
1812 }
1813 return self.structFieldOffset(field_count, target);
1814 },
1809 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {1815 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
1810 var buffer: Payload.Bits = undefined;1816 var buffer: Payload.Bits = undefined;
1811 const int_tag_ty = self.intTagType(&buffer);1817 const int_tag_ty = self.intTagType(&buffer);
test/behavior.zig+2-1
...@@ -6,6 +6,7 @@ test {...@@ -6,6 +6,7 @@ test {
6 _ = @import("behavior/bool.zig");6 _ = @import("behavior/bool.zig");
7 _ = @import("behavior/if.zig");7 _ = @import("behavior/if.zig");
8 _ = @import("behavior/truncate.zig");8 _ = @import("behavior/truncate.zig");
9 _ = @import("behavior/null.zig");
910
10 if (builtin.object_format != .c) {11 if (builtin.object_format != .c) {
11 // Tests that pass for stage1 and stage2 but not the C backend.12 // Tests that pass for stage1 and stage2 but not the C backend.
...@@ -48,7 +49,7 @@ test {...@@ -48,7 +49,7 @@ test {
48 _ = @import("behavior/math.zig");49 _ = @import("behavior/math.zig");
49 _ = @import("behavior/maximum_minimum.zig");50 _ = @import("behavior/maximum_minimum.zig");
50 _ = @import("behavior/member_func.zig");51 _ = @import("behavior/member_func.zig");
51 _ = @import("behavior/null.zig");52 _ = @import("behavior/null_llvm.zig");
52 _ = @import("behavior/optional.zig");53 _ = @import("behavior/optional.zig");
53 _ = @import("behavior/pointers.zig");54 _ = @import("behavior/pointers.zig");
54 _ = @import("behavior/popcount.zig");55 _ = @import("behavior/popcount.zig");
test/behavior/null.zig+14-26
...@@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool {...@@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool {
59 return value > 1234;59 return value > 1234;
60}60}
6161
62test "null literal outside function" {
63 const is_null = here_is_a_null_literal.context == null;
64 try expect(is_null);
65
66 const is_non_null = here_is_a_null_literal.context != null;
67 try expect(!is_non_null);
68}
69const SillyStruct = struct {
70 context: ?i32,
71};
72const here_is_a_null_literal = SillyStruct{ .context = null };
73
74test "test null runtime" {62test "test null runtime" {
75 try testTestNullRuntime(null);63 try testTestNullRuntime(null);
76}64}
...@@ -97,23 +85,23 @@ fn bar(x: ?void) ?void {...@@ -97,23 +85,23 @@ fn bar(x: ?void) ?void {
97 }85 }
98}86}
9987
100const StructWithOptional = struct {88const Empty = struct {};
101 field: ?i32,
102};
10389
104var struct_with_optional: StructWithOptional = undefined;90test "optional struct{}" {
91 _ = try optionalEmptyStructImpl();
92 _ = comptime try optionalEmptyStructImpl();
93}
10594
106test "unwrap optional which is field of global var" {95fn optionalEmptyStructImpl() !void {
107 struct_with_optional.field = null;96 try expect(baz(null) == null);
108 if (struct_with_optional.field) |payload| {97 try expect(baz(Empty{}) != null);
109 _ = payload;98}
110 unreachable;99
111 }100fn baz(x: ?Empty) ?Empty {
112 struct_with_optional.field = 1234;101 if (x) |_| {
113 if (struct_with_optional.field) |payload| {102 return Empty{};
114 try expect(payload == 1234);
115 } else {103 } else {
116 unreachable;104 return null;
117 }105 }
118}106}
119107
test/behavior/null_llvm.zig created+36
...@@ -0,0 +1,36 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "null literal outside function" {
5 const is_null = here_is_a_null_literal.context == null;
6 try expect(is_null);
7
8 const is_non_null = here_is_a_null_literal.context != null;
9 try expect(!is_non_null);
10}
11
12const SillyStruct = struct {
13 context: ?i32,
14};
15
16const here_is_a_null_literal = SillyStruct{ .context = null };
17
18const StructWithOptional = struct {
19 field: ?i32,
20};
21
22var struct_with_optional: StructWithOptional = undefined;
23
24test "unwrap optional which is field of global var" {
25 struct_with_optional.field = null;
26 if (struct_with_optional.field) |payload| {
27 _ = payload;
28 unreachable;
29 }
30 struct_with_optional.field = 1234;
31 if (struct_with_optional.field) |payload| {
32 try expect(payload == 1234);
33 } else {
34 unreachable;
35 }
36}