Commit 674932e503

Emily Bellows <emily.a.bellows@hey.com>
2021-10-31 19:21:02
C backend: implement ?void, and other zero sized types
1 parent 325bae7
Changed files (5)
src/codegen/c.zig
@@ -308,6 +308,11 @@ pub const DeclGen = struct {
                 if (ty.isPtrLikeOptional()) {
                     return dg.renderValue(writer, payload_type, val);
                 }
+                const target = dg.module.getTarget();
+                if (payload_type.abiSize(target) == 0) {
+                    const is_null = val.castTag(.opt_payload) == null;
+                    return writer.print("{}", .{is_null});
+                }
                 try writer.writeByte('(');
                 try dg.renderType(writer, ty);
                 try writer.writeAll("){");
@@ -588,10 +593,13 @@ pub const DeclGen = struct {
             .Optional => {
                 var opt_buf: Type.Payload.ElemType = undefined;
                 const child_type = t.optionalChild(&opt_buf);
+                const target = dg.module.getTarget();
                 if (t.isPtrLikeOptional()) {
                     return dg.renderType(w, child_type);
                 } else if (dg.typedefs.get(t)) |some| {
                     return w.writeAll(some.name);
+                } else if (child_type.abiSize(target) == 0) {
+                    return w.writeAll("bool");
                 }
 
                 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
@@ -2100,14 +2108,21 @@ fn airIsNull(
     const un_op = f.air.instructions.items(.data)[inst].un_op;
     const writer = f.object.writer();
     const operand = try f.resolveInst(un_op);
+    const target = f.object.dg.module.getTarget();
 
     const local = try f.allocLocal(Type.initTag(.bool), .Const);
     try writer.writeAll(" = (");
     try f.writeCValue(writer, operand);
 
-    if (f.air.typeOf(un_op).isPtrLikeOptional()) {
+    const ty = f.air.typeOf(un_op);
+    var opt_buf: Type.Payload.ElemType = undefined;
+    const payload_type = ty.optionalChild(&opt_buf);
+
+    if (ty.isPtrLikeOptional()) {
         // operand is a regular pointer, test `operand !=/== NULL`
         try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
+    } else if (payload_type.abiSize(target) == 0) {
+        try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
     } else {
         try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
     }
src/type.zig
@@ -1805,7 +1805,13 @@ pub const Type = extern union {
             .void,
             => 0,
 
-            .@"struct" => return self.structFieldOffset(self.structFieldCount(), target),
+            .@"struct" => {
+                const field_count = self.structFieldCount();
+                if (field_count == 0) {
+                    return 0;
+                }
+                return self.structFieldOffset(field_count, target);
+            },
             .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
                 var buffer: Payload.Bits = undefined;
                 const int_tag_ty = self.intTagType(&buffer);
test/behavior/null.zig
@@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool {
     return value > 1234;
 }
 
-test "null literal outside function" {
-    const is_null = here_is_a_null_literal.context == null;
-    try expect(is_null);
-
-    const is_non_null = here_is_a_null_literal.context != null;
-    try expect(!is_non_null);
-}
-const SillyStruct = struct {
-    context: ?i32,
-};
-const here_is_a_null_literal = SillyStruct{ .context = null };
-
 test "test null runtime" {
     try testTestNullRuntime(null);
 }
@@ -97,23 +85,23 @@ fn bar(x: ?void) ?void {
     }
 }
 
-const StructWithOptional = struct {
-    field: ?i32,
-};
+const Empty = struct {};
 
-var struct_with_optional: StructWithOptional = undefined;
+test "optional struct{}" {
+    _ = try optionalEmptyStructImpl();
+    _ = comptime try optionalEmptyStructImpl();
+}
 
-test "unwrap optional which is field of global var" {
-    struct_with_optional.field = null;
-    if (struct_with_optional.field) |payload| {
-        _ = payload;
-        unreachable;
-    }
-    struct_with_optional.field = 1234;
-    if (struct_with_optional.field) |payload| {
-        try expect(payload == 1234);
+fn optionalEmptyStructImpl() !void {
+    try expect(baz(null) == null);
+    try expect(baz(Empty{}) != null);
+}
+
+fn baz(x: ?Empty) ?Empty {
+    if (x) |_| {
+        return Empty{};
     } else {
-        unreachable;
+        return null;
     }
 }
 
test/behavior/null_llvm.zig
@@ -0,0 +1,36 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "null literal outside function" {
+    const is_null = here_is_a_null_literal.context == null;
+    try expect(is_null);
+
+    const is_non_null = here_is_a_null_literal.context != null;
+    try expect(!is_non_null);
+}
+
+const SillyStruct = struct {
+    context: ?i32,
+};
+
+const here_is_a_null_literal = SillyStruct{ .context = null };
+
+const StructWithOptional = struct {
+    field: ?i32,
+};
+
+var struct_with_optional: StructWithOptional = undefined;
+
+test "unwrap optional which is field of global var" {
+    struct_with_optional.field = null;
+    if (struct_with_optional.field) |payload| {
+        _ = payload;
+        unreachable;
+    }
+    struct_with_optional.field = 1234;
+    if (struct_with_optional.field) |payload| {
+        try expect(payload == 1234);
+    } else {
+        unreachable;
+    }
+}
test/behavior.zig
@@ -6,6 +6,7 @@ test {
     _ = @import("behavior/bool.zig");
     _ = @import("behavior/if.zig");
     _ = @import("behavior/truncate.zig");
+    _ = @import("behavior/null.zig");
 
     if (builtin.object_format != .c) {
         // Tests that pass for stage1 and stage2 but not the C backend.
@@ -48,7 +49,7 @@ test {
         _ = @import("behavior/math.zig");
         _ = @import("behavior/maximum_minimum.zig");
         _ = @import("behavior/member_func.zig");
-        _ = @import("behavior/null.zig");
+        _ = @import("behavior/null_llvm.zig");
         _ = @import("behavior/optional.zig");
         _ = @import("behavior/pointers.zig");
         _ = @import("behavior/popcount.zig");