stage2: fix if expressions on error unions

AstGen had the then-else logic backwards for if expressions
on error unions. This commit fixes it.

Turns out AstGen only really needs `is_non_null` and `is_non_err`,
and does not need the `is_null` or `is_err` variants. So I removed the
`is_null{,_ptr}` and `is_err{,_ptr}` ZIR instructions (-4) and
added `is_non_err`, `is_non_err_ptr` ZIR instructions (+2) for
a total of (-2) ZIR instructions, giving us a tiny bit more headroom
within the 256 tag limit. This required swapping the order of
then/else blocks in a handful of cases, but ultimately means the
ZIR will be in the same as source order, which is convenient
when debugging.

AIR code on the other hand, gains the `is_non_err` and `is_non_err_ptr`
instructions.

Sema: fix logic in zirErrUnionCode and zirErrUnionCodePtr returning the
wrong result type.
This commit is contained in:
Andrew Kelley
2021-07-07 19:50:56 -07:00
parent 5816997ae7
commit 5c8bd443d9
7 changed files with 119 additions and 104 deletions

View File

@@ -895,8 +895,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
.ref => try genRef(o, inst.castTag(.ref).?),
.struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
.is_err => try genIsErr(o, inst.castTag(.is_err).?),
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
.is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="),
.is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="),
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="),
.is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="),
.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
@@ -1446,15 +1448,14 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
return local;
}
fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
fn genIsErr(o: *Object, inst: *Inst.UnOp, deref_suffix: []const u8, op_str: []const u8) !CValue {
const writer = o.writer();
const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else "";
const operand = try o.resolveInst(inst.operand);
const local = try o.allocLocal(Type.initTag(.bool), .Const);
try writer.writeAll(" = (");
try o.writeCValue(writer, operand);
try writer.print("){s}.error != 0;\n", .{maybe_deref});
try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str });
return local;
}

View File

@@ -814,7 +814,8 @@ pub const Context = struct {
.constant => unreachable,
.dbg_stmt => WValue.none,
.div => self.genBinOp(inst.castTag(.div).?, .div),
.is_err => self.genIsErr(inst.castTag(.is_err).?),
.is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
.is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
.load => self.genLoad(inst.castTag(.load).?),
.loop => self.genLoop(inst.castTag(.loop).?),
.mul => self.genBinOp(inst.castTag(.mul).?, .mul),
@@ -1278,7 +1279,7 @@ pub const Context = struct {
return .none;
}
fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {
const operand = self.resolveInst(inst.operand);
const offset = self.code.items.len;
const writer = self.code.writer();
@@ -1289,9 +1290,7 @@ pub const Context = struct {
try writer.writeByte(wasm.opcode(.i32_const));
try leb.writeILEB128(writer, @as(i32, 0));
// we want to break out of the condition if they're *not* equal,
// because that means there's an error.
try writer.writeByte(wasm.opcode(.i32_ne));
try writer.writeByte(@enumToInt(opcode));
return WValue{ .code_offset = offset };
}