Commit 2e15a404e2

Scibuild <30519309+Scibuild@users.noreply.github.com>
2021-11-17 22:26:15
C backend: errors and optionals
* bitcast treats all pointers as pointers * correctly unwrapping error unions with pointers * equality operators for primitive optional types
1 parent 8f1e417
Changed files (4)
src/codegen/c.zig
@@ -1162,12 +1162,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
 
             .slice => try airSlice(f, inst),
 
-            .cmp_eq  => try airBinOp(f, inst, " == "),
+            .cmp_eq  => try airEquality(f, inst, .cmp_eq),
             .cmp_gt  => try airBinOp(f, inst, " > "),
             .cmp_gte => try airBinOp(f, inst, " >= "),
             .cmp_lt  => try airBinOp(f, inst, " < "),
             .cmp_lte => try airBinOp(f, inst, " <= "),
-            .cmp_neq => try airBinOp(f, inst, " != "),
+            .cmp_neq => try airEquality(f, inst, .cmp_neq),
 
             // bool_and and bool_or are non-short-circuit operations
             .bool_and        => try airBinOp(f, inst, " & "),
@@ -1257,9 +1257,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .slice_elem_ptr     => try airSliceElemPtr(f, inst),
             .array_elem_val     => try airArrayElemVal(f, inst),
 
-            .unwrap_errunion_payload     => try airUnwrapErrUnionPay(f, inst),
+            .unwrap_errunion_payload     => try airUnwrapErrUnionPay(f, inst, ""),
             .unwrap_errunion_err         => try airUnwrapErrUnionErr(f, inst),
-            .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst),
+            .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, "&"),
             .unwrap_errunion_err_ptr     => try airUnwrapErrUnionErr(f, inst),
             .wrap_errunion_payload       => try airWrapErrUnionPay(f, inst),
             .wrap_errunion_err           => try airWrapErrUnionErr(f, inst),
@@ -1908,6 +1908,51 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
     return local;
 }
 
+fn airEquality(f: *Function, inst: Air.Inst.Index, op: Air.Inst.Tag) !CValue {
+    if (f.liveness.isUnused(inst))
+        return CValue.none;
+
+    const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+    const lhs = try f.resolveInst(bin_op.lhs);
+    const rhs = try f.resolveInst(bin_op.rhs);
+
+    const writer = f.object.writer();
+    const inst_ty = f.air.typeOfIndex(inst);
+    const local = try f.allocLocal(inst_ty, .Const);
+
+    try writer.writeAll(" = ");
+
+    const lhs_ty = f.air.typeOf(bin_op.lhs);
+    if (lhs_ty.tag() == .optional) {
+        // (A && B)  || (C && (A == B))
+        // A = lhs.is_null  ;  B = rhs.is_null  ;  C = rhs.payload == lhs.payload
+
+        try writer.writeAll(if (op == .cmp_eq) "((" else "!((");
+        try f.writeCValue(writer, lhs);
+        try writer.writeAll(".is_null && ");
+        try f.writeCValue(writer, rhs);
+        try writer.writeAll(".is_null) || (");
+        try f.writeCValue(writer, lhs);
+        try writer.writeAll(".payload == ");
+        try f.writeCValue(writer, rhs);
+        try writer.writeAll(".payload && ");
+        try f.writeCValue(writer, lhs);
+        try writer.writeAll(".is_null == ");
+        try f.writeCValue(writer, rhs);
+        try writer.writeAll(".is_null));\n");
+
+        return local;
+    }
+
+    const operator = if (op == .cmp_eq) "==" else "!=";
+    try f.writeCValue(writer, lhs);
+    try writer.print("{s}", .{operator});
+    try f.writeCValue(writer, rhs);
+    try writer.writeAll(";\n");
+
+    return local;
+}
+
 fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
@@ -2104,8 +2149,8 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
 
     const writer = f.object.writer();
     const inst_ty = f.air.typeOfIndex(inst);
-    if (inst_ty.zigTypeTag() == .Pointer and
-        f.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)
+    if (inst_ty.isPtrAtRuntime() and
+        f.air.typeOf(ty_op.operand).isPtrAtRuntime())
     {
         const local = try f.allocLocal(inst_ty, .Const);
         try writer.writeAll(" = (");
@@ -2503,7 +2548,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
     return local;
 }
 
-fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
+fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []const u8) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
 
@@ -2519,7 +2564,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
 
     const inst_ty = f.air.typeOfIndex(inst);
     const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
-    const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
 
     const local = try f.allocLocal(inst_ty, .Const);
     try writer.print(" = {s}(", .{maybe_addrof});
test/behavior/optional.zig
@@ -18,25 +18,6 @@ test "passing an optional integer as a parameter" {
     comptime try expect(S.entry());
 }
 
-test "self-referential struct through a slice of optional" {
-    const S = struct {
-        const Node = struct {
-            children: []?Node,
-            data: ?u8,
-
-            fn new() Node {
-                return Node{
-                    .children = undefined,
-                    .data = null,
-                };
-            }
-        };
-    };
-
-    var n = S.Node.new();
-    try expect(n.data == null);
-}
-
 pub const EmptyStruct = struct {};
 
 test "optional pointer to size zero struct" {
test/behavior/optional_llvm.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+const testing = std.testing;
+const expect = testing.expect;
+const expectEqual = testing.expectEqual;
+
+test "self-referential struct through a slice of optional" {
+    const S = struct {
+        const Node = struct {
+            children: []?Node,
+            data: ?u8,
+
+            fn new() Node {
+                return Node{
+                    .children = undefined,
+                    .data = null,
+                };
+            }
+        };
+    };
+
+    var n = S.Node.new();
+    try expect(n.data == null);
+}
test/behavior.zig
@@ -41,6 +41,8 @@ test {
     _ = @import("behavior/member_func.zig");
     _ = @import("behavior/translate_c_macros.zig");
     _ = @import("behavior/generics.zig");
+    _ = @import("behavior/error.zig");
+    _ = @import("behavior/optional.zig");
 
     if (builtin.object_format != .c) {
         // Tests that pass for stage1 and stage2 but not the C backend.
@@ -55,6 +57,7 @@ test {
         _ = @import("behavior/bugs/2006.zig");
         _ = @import("behavior/bugs/3112.zig");
         _ = @import("behavior/cast_llvm.zig");
+        _ = @import("behavior/error.zig");
         _ = @import("behavior/eval.zig");
         _ = @import("behavior/floatop.zig");
         _ = @import("behavior/fn.zig");
@@ -63,7 +66,7 @@ test {
         _ = @import("behavior/math.zig");
         _ = @import("behavior/maximum_minimum.zig");
         _ = @import("behavior/null_llvm.zig");
-        _ = @import("behavior/optional.zig");
+        _ = @import("behavior/optional_llvm.zig");
         _ = @import("behavior/popcount.zig");
         _ = @import("behavior/saturating_arithmetic.zig");
         _ = @import("behavior/sizeof_and_typeof.zig");