mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 13:58:27 +00:00
stage2: implement isErr/isNonErr and unwrap error
This commit is contained in:
parent
818672312f
commit
e9f069f536
@ -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}),
|
||||
}
|
||||
|
||||
@ -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;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user