Commit 30ffa052f2

jacob gw <jacoblevgw@gmail.com>
2021-03-01 21:16:18
stage2 cbe: add error union and error union operations
1 parent 6467ef6
Changed files (4)
src
codegen
link
test
stage2
src/codegen/c.zig
@@ -251,6 +251,31 @@ pub const DeclGen = struct {
                 // error values will be #defined at the top of the file
                 return writer.print("zig_error_{s}", .{payload.data.name});
             },
+            .ErrorUnion => {
+                const error_type = t.errorUnionSet();
+                const payload_type = t.errorUnionChild();
+                const data = val.castTag(.error_union).?.data;
+                try writer.writeByte('(');
+                try dg.renderType(writer, t);
+                try writer.writeAll("){");
+                if (val.getError()) |_| {
+                    try writer.writeAll(" .error = ");
+                    try dg.renderValue(
+                        writer,
+                        error_type,
+                        data,
+                    );
+                    try writer.writeAll(" }");
+                } else {
+                    try writer.writeAll(" .payload = ");
+                    try dg.renderValue(
+                        writer,
+                        payload_type,
+                        data,
+                    );
+                    try writer.writeAll(", .error = 0 }");
+                }
+            },
             else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
                 @tagName(e),
             }),
@@ -385,16 +410,17 @@ pub const DeclGen = struct {
                     return w.writeAll(some.name);
                 }
                 const child_type = t.errorUnionChild();
+                const set_type = t.errorUnionSet();
 
                 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
                 defer buffer.deinit();
                 const bw = buffer.writer();
 
                 try bw.writeAll("typedef struct { ");
-                try dg.renderType(bw, t.errorUnionChild());
+                try dg.renderType(bw, child_type);
                 try bw.writeAll(" payload; uint16_t error; } ");
                 const name_index = buffer.items.len;
-                try bw.print("zig_err_union_{s}_t;\n", .{typeToCIdentifier(child_type)});
+                try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) });
 
                 const rendered = buffer.toOwnedSlice();
                 errdefer dg.typedefs.allocator.free(rendered);
@@ -543,6 +569,12 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
             .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload).?),
             .is_err => try genIsErr(o, inst.castTag(.is_err).?),
             .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
+            .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
+            .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
+            .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
+            .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),
+            .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),
+            .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
             else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
         };
         switch (result_value) {
@@ -962,6 +994,35 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
     return local;
 }
 
+// *(E!T) -> E NOT *E
+fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const operand = try o.resolveInst(inst.operand);
+
+    const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
+
+    const local = try o.allocLocal(inst.base.ty, .Const);
+    try writer.writeAll(" = (");
+    try o.writeCValue(writer, operand);
+
+    try writer.print("){s}error;\n", .{maybe_deref});
+    return local;
+}
+fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const operand = try o.resolveInst(inst.operand);
+
+    const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
+    const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
+
+    const local = try o.allocLocal(inst.base.ty, .Const);
+    try writer.print(" = {s}(", .{maybe_addrof});
+    try o.writeCValue(writer, operand);
+
+    try writer.print("){s}payload;\n", .{maybe_deref});
+    return local;
+}
+
 fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue {
     const writer = o.writer();
     const operand = try o.resolveInst(inst.operand);
@@ -978,6 +1039,26 @@ fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue {
     try writer.writeAll("};\n");
     return local;
 }
+fn genWrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const operand = try o.resolveInst(inst.operand);
+
+    const local = try o.allocLocal(inst.base.ty, .Const);
+    try writer.writeAll(" = { .error = ");
+    try o.writeCValue(writer, operand);
+    try writer.writeAll(" };\n");
+    return local;
+}
+fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
+    const writer = o.writer();
+    const operand = try o.resolveInst(inst.operand);
+
+    const local = try o.allocLocal(inst.base.ty, .Const);
+    try writer.writeAll(" = { .error = 0, .payload = ");
+    try o.writeCValue(writer, operand);
+    try writer.writeAll(" };\n");
+    return local;
+}
 
 fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
     const writer = o.writer();
src/link/C.zig
@@ -179,7 +179,8 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
         if (module.global_error_set.size == 0) break :render_errors;
         var it = module.global_error_set.iterator();
         while (it.next()) |entry| {
-            try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
+            // + 1 because 0 represents no error
+            try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 });
         }
         try err_typedef_writer.writeByte('\n');
     }
src/type.zig
@@ -1825,6 +1825,17 @@ pub const Type = extern union {
         };
     }
 
+    pub fn errorUnionSet(self: Type) Type {
+        return switch (self.tag()) {
+            .anyerror_void_error_union => Type.initTag(.anyerror),
+            .error_union => {
+                const payload = self.castTag(.error_union).?;
+                return payload.data.error_set;
+            },
+            else => unreachable,
+        };
+    }
+
     /// Asserts the type is an array or vector.
     pub fn arrayLen(self: Type) u64 {
         return switch (self.tag()) {
test/stage2/cbe.zig
@@ -286,6 +286,20 @@ pub fn addCases(ctx: *TestContext) !void {
             \\    if (!b) unreachable;
             \\}
         , "");
+        case.addCompareOutput(
+            \\export fn main() c_int {
+            \\    var e: anyerror!c_int = 0;
+            \\    const i = e catch 69;
+            \\    return i;
+            \\}
+        , "");
+        case.addCompareOutput(
+            \\export fn main() c_int {
+            \\    var e: anyerror!c_int = error.Foo;
+            \\    const i = e catch 69;
+            \\    return 69 - i;
+            \\}
+        , "");
     }
     ctx.c("empty start function", linux_x64,
         \\export fn _start() noreturn {