Commit 30ffa052f2
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 {