stage2 cbe: add error union and error union operations

This commit is contained in:
jacob gw 2021-03-01 15:16:18 -05:00 committed by Veikka Tuominen
parent 6467ef6d3b
commit 30ffa052f2
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
4 changed files with 110 additions and 3 deletions

View File

@ -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();

View File

@ -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');
}

View File

@ -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()) {

View File

@ -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 {