stage2: implement isErr/isNonErr and unwrap error

This commit is contained in:
Jakub Konka 2022-01-03 17:05:56 +01:00
parent 818672312f
commit e9f069f536
2 changed files with 89 additions and 39 deletions

View File

@ -415,9 +415,14 @@ fn gen(self: *Self) InnerError!void {
try self.genBody(self.air.getMainBody());
if (self.exitlude_jump_relocs.items.len == 1) {
self.mir_instructions.len -= 1;
} else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
// TODO can single exitlude jump reloc be elided? What if it is not at the end of the code?
// Example:
// pub fn main() void {
// maybeErr() catch return;
// unreachable;
// }
// Eliding the reloc will cause a miscompilation in this case.
for (self.exitlude_jump_relocs.items) |jmp_reloc| {
self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len);
}
@ -1180,19 +1185,24 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst))
.dead
else
return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
const err_union_ty = self.air.typeOf(ty_op.operand);
const payload_ty = err_union_ty.errorUnionPayload();
const mcv = try self.resolveInst(ty_op.operand);
if (!payload_ty.hasCodeGenBits()) break :result mcv;
return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst))
.dead
else
return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
const err_union_ty = self.air.typeOf(ty_op.operand);
const payload_ty = err_union_ty.errorUnionPayload();
if (!payload_ty.hasCodeGenBits()) break :result MCValue.none;
return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
@ -2396,19 +2406,35 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
}
fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
_ = ty;
_ = operand;
// Here you can specialize this instruction if it makes sense to, otherwise the default
// will call isNonErr and invert the result.
return self.fail("TODO call isNonErr and invert the result", .{});
const err_type = ty.errorUnionSet();
const payload_type = ty.errorUnionPayload();
if (!err_type.hasCodeGenBits()) {
return MCValue{ .immediate = 0 }; // always false
} else if (!payload_type.hasCodeGenBits()) {
if (err_type.abiSize(self.target.*) <= 8) {
try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 });
return MCValue{ .compare_flags_unsigned = .gt };
} else {
return self.fail("TODO isErr for errors with size larger than register size", .{});
}
} else {
return self.fail("TODO isErr for non-empty payloads", .{});
}
}
fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
_ = ty;
_ = operand;
// Here you can specialize this instruction if it makes sense to, otherwise the default
// will call isErr and invert the result.
return self.fail("TODO call isErr and invert the result", .{});
const is_err_res = try self.isErr(ty, operand);
switch (is_err_res) {
.compare_flags_unsigned => |op| {
assert(op == .gt);
return MCValue{ .compare_flags_unsigned = .lte };
},
.immediate => |imm| {
assert(imm == 0);
return MCValue{ .immediate = 1 };
},
else => unreachable,
}
}
fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
@ -3435,31 +3461,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
}
},
.ErrorSet => {
switch (typed_value.val.tag()) {
.@"error" => {
const err_name = typed_value.val.castTag(.@"error").?.data.name;
const module = self.bin_file.options.module.?;
const global_error_set = module.global_error_set;
const error_index = global_error_set.get(err_name).?;
return MCValue{ .immediate = error_index };
},
else => {
// In this case we are rendering an error union which has a 0 bits payload.
return MCValue{ .immediate = 0 };
},
}
const err_name = typed_value.val.castTag(.@"error").?.data.name;
const module = self.bin_file.options.module.?;
const global_error_set = module.global_error_set;
const error_index = global_error_set.get(err_name).?;
return MCValue{ .immediate = error_index };
},
.ErrorUnion => {
const error_type = typed_value.ty.errorUnionSet();
const payload_type = typed_value.ty.errorUnionPayload();
const sub_val = typed_value.val.castTag(.eu_payload).?.data;
if (!payload_type.hasCodeGenBits()) {
// We use the error type directly as the type.
return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
if (typed_value.val.castTag(.eu_payload)) |pl| {
if (!payload_type.hasCodeGenBits()) {
// We use the error type directly as the type.
return MCValue{ .immediate = 0 };
}
_ = pl;
return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty});
} else {
if (!payload_type.hasCodeGenBits()) {
// We use the error type directly as the type.
return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
}
}
return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});
return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty});
},
else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
}

View File

@ -1738,6 +1738,29 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, "");
}
{
var case = ctx.exe("unwrap error union - simple errors", target);
case.addCompareOutput(
\\pub fn main() void {
\\ maybeErr() catch unreachable;
\\}
\\
\\fn maybeErr() !void {
\\ return;
\\}
, "");
case.addCompareOutput(
\\pub fn main() void {
\\ maybeErr() catch return;
\\ unreachable;
\\}
\\
\\fn maybeErr() !void {
\\ return error.NoWay;
\\}
, "");
}
}
}