diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 084c653d1c..ceafe01d35 100644 --- a/src/codegen/c.zig +++ b/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(); diff --git a/src/link/C.zig b/src/link/C.zig index 60844ce43b..655a044394 100644 --- a/src/link/C.zig +++ b/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'); } diff --git a/src/type.zig b/src/type.zig index c3a99bb184..b9f9207d4a 100644 --- a/src/type.zig +++ b/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()) { diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 1487b15e12..e9082f57fa 100644 --- a/test/stage2/cbe.zig +++ b/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 {