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

@@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
rl,
node,
node_datas[node].lhs,
.is_err_ptr,
.is_non_err_ptr,
.err_union_payload_unsafe_ptr,
.err_union_code_ptr,
node_datas[node].rhs,
@@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
rl,
node,
node_datas[node].lhs,
.is_err,
.is_non_err,
.err_union_payload_unsafe,
.err_union_code,
node_datas[node].rhs,
@@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
rl,
node,
node_datas[node].lhs,
.is_null_ptr,
.is_non_null_ptr,
.optional_payload_unsafe_ptr,
undefined,
node_datas[node].rhs,
@@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
rl,
node,
node_datas[node].lhs,
.is_null,
.is_non_null,
.optional_payload_unsafe,
undefined,
node_datas[node].rhs,
@@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
.float128,
.int_type,
.is_non_null,
.is_null,
.is_non_null_ptr,
.is_null_ptr,
.is_err,
.is_err_ptr,
.is_non_err,
.is_non_err_ptr,
.mod_rem,
.mul,
.mulwrap,
@@ -4621,8 +4619,8 @@ fn tryExpr(
};
const err_ops = switch (rl) {
// zig fmt: off
.ref => [3]Zir.Inst.Tag{ .is_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
else => [3]Zir.Inst.Tag{ .is_err, .err_union_code, .err_union_payload_unsafe },
.ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe },
// zig fmt: on
};
// This could be a pointer or value depending on the `operand_rl` parameter.
@@ -4640,20 +4638,20 @@ fn tryExpr(
var then_scope = parent_gz.makeSubBlock(scope);
defer then_scope.instructions.deinit(astgen.gpa);
const err_code = try then_scope.addUnNode(err_ops[1], operand, node);
try genDefers(&then_scope, &fn_block.base, scope, .{ .both = err_code });
const then_result = try then_scope.addUnNode(.ret_node, err_code, node);
block_scope.break_count += 1;
// This could be a pointer or value depending on `err_ops[2]`.
const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
const then_result = switch (rl) {
.ref => unwrapped_payload,
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
};
var else_scope = parent_gz.makeSubBlock(scope);
defer else_scope.instructions.deinit(astgen.gpa);
block_scope.break_count += 1;
// This could be a pointer or value depending on `err_ops[2]`.
const unwrapped_payload = try else_scope.addUnNode(err_ops[2], operand, node);
const else_result = switch (rl) {
.ref => unwrapped_payload,
else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
};
const err_code = try else_scope.addUnNode(err_ops[1], operand, node);
try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
const else_result = try else_scope.addUnNode(.ret_node, err_code, node);
return finishThenElseBlock(
parent_gz,
@@ -4711,18 +4709,28 @@ fn orelseCatchExpr(
var then_scope = parent_gz.makeSubBlock(scope);
defer then_scope.instructions.deinit(astgen.gpa);
// This could be a pointer or value depending on `unwrap_op`.
const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
const then_result = switch (rl) {
.ref => unwrapped_payload,
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
};
var else_scope = parent_gz.makeSubBlock(scope);
defer else_scope.instructions.deinit(astgen.gpa);
var err_val_scope: Scope.LocalVal = undefined;
const then_sub_scope = blk: {
const payload = payload_token orelse break :blk &then_scope.base;
const else_sub_scope = blk: {
const payload = payload_token orelse break :blk &else_scope.base;
if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
}
const err_name = try astgen.identAsString(payload);
err_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
.parent = &else_scope.base,
.gen_zir = &else_scope,
.name = err_name,
.inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
.inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
.token_src = payload,
.id_cat = .@"capture",
};
@@ -4730,23 +4738,13 @@ fn orelseCatchExpr(
};
block_scope.break_count += 1;
const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs);
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
// We hold off on the break instructions as well as copying the then/else
// instructions into place until we know whether to keep store_to_block_ptr
// instructions or not.
var else_scope = parent_gz.makeSubBlock(scope);
defer else_scope.instructions.deinit(astgen.gpa);
// This could be a pointer or value depending on `unwrap_op`.
const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
const else_result = switch (rl) {
.ref => unwrapped_payload,
else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
};
return finishThenElseBlock(
parent_gz,
rl,
@@ -4964,7 +4962,7 @@ fn ifExpr(
if (if_full.error_token) |_| {
const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err;
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
break :c .{
.inst = err_union,
.bool_bit = try block_scope.addUnNode(tag, err_union, node),
@@ -5221,7 +5219,7 @@ fn whileExpr(
if (while_full.error_token) |_| {
const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err;
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
break :c .{
.inst = err_union,
.bool_bit = try continue_scope.addUnNode(tag, err_union, node),
@@ -6229,23 +6227,25 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
}
// Emit conditional branch for generating errdefers.
const is_err = try gz.addUnNode(.is_err, operand, node);
const is_non_err = try gz.addUnNode(.is_non_err, operand, node);
const condbr = try gz.addCondBr(.condbr, node);
var then_scope = gz.makeSubBlock(scope);
defer then_scope.instructions.deinit(astgen.gpa);
const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
.both = try then_scope.addUnNode(.err_union_code, operand, node),
};
try genDefers(&then_scope, defer_outer, scope, which_ones);
try genDefers(&then_scope, defer_outer, scope, .normal_only);
_ = try then_scope.addUnNode(.ret_node, operand, node);
var else_scope = gz.makeSubBlock(scope);
defer else_scope.instructions.deinit(astgen.gpa);
try genDefers(&else_scope, defer_outer, scope, .normal_only);
const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
.both = try else_scope.addUnNode(.err_union_code, operand, node),
};
try genDefers(&else_scope, defer_outer, scope, which_ones);
_ = try else_scope.addUnNode(.ret_node, operand, node);
try setCondBrPayload(condbr, is_err, &then_scope, &else_scope);
try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
return Zir.Inst.Ref.unreachable_value;
},

View File

@@ -225,12 +225,10 @@ pub fn analyzeBody(
.float => try sema.zirFloat(block, inst),
.float128 => try sema.zirFloat128(block, inst),
.int_type => try sema.zirIntType(block, inst),
.is_err => try sema.zirIsErr(block, inst),
.is_err_ptr => try sema.zirIsErrPtr(block, inst),
.is_non_null => try sema.zirIsNull(block, inst, true),
.is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true),
.is_null => try sema.zirIsNull(block, inst, false),
.is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
.is_non_err => try sema.zirIsNonErr(block, inst),
.is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst),
.is_non_null => try sema.zirIsNonNull(block, inst),
.is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
.loop => try sema.zirLoop(block, inst),
.merge_error_sets => try sema.zirMergeErrorSets(block, inst),
.negate => try sema.zirNegate(block, inst, .sub),
@@ -2981,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
if (operand.ty.zigTypeTag() != .ErrorUnion)
return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
const result_ty = operand.ty.castTag(.error_union).?.data.error_set;
if (operand.value()) |val| {
assert(val.getError() != null);
const data = val.castTag(.error_union).?.data;
return sema.mod.constInst(sema.arena, src, .{
.ty = operand.ty.castTag(.error_union).?.data.error_set,
.ty = result_ty,
.val = data,
});
}
try sema.requireRuntimeBlock(block, src);
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand);
}
/// Pointer in, value out
@@ -3007,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set;
if (operand.value()) |pointer_val| {
const val = try pointer_val.pointerDeref(sema.arena);
assert(val.getError() != null);
const data = val.castTag(.error_union).?.data;
return sema.mod.constInst(sema.arena, src, .{
.ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
.ty = result_ty,
.val = data,
});
}
try sema.requireRuntimeBlock(block, src);
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand);
}
fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
@@ -5298,11 +5300,10 @@ fn zirBoolBr(
return &block_inst.base;
}
fn zirIsNull(
fn zirIsNonNull(
sema: *Sema,
block: *Scope.Block,
inst: Zir.Inst.Index,
invert_logic: bool,
) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -5310,14 +5311,13 @@ fn zirIsNull(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand = try sema.resolveInst(inst_data.operand);
return sema.analyzeIsNull(block, src, operand, invert_logic);
return sema.analyzeIsNull(block, src, operand, true);
}
fn zirIsNullPtr(
fn zirIsNonNullPtr(
sema: *Sema,
block: *Scope.Block,
inst: Zir.Inst.Index,
invert_logic: bool,
) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -5326,19 +5326,19 @@ fn zirIsNullPtr(
const src = inst_data.src();
const ptr = try sema.resolveInst(inst_data.operand);
const loaded = try sema.analyzeLoad(block, src, ptr, src);
return sema.analyzeIsNull(block, src, loaded, invert_logic);
return sema.analyzeIsNull(block, src, loaded, true);
}
fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const operand = try sema.resolveInst(inst_data.operand);
return sema.analyzeIsErr(block, inst_data.src(), operand);
return sema.analyzeIsNonErr(block, inst_data.src(), operand);
}
fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -5346,7 +5346,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
const src = inst_data.src();
const ptr = try sema.resolveInst(inst_data.operand);
const loaded = try sema.analyzeLoad(block, src, ptr, src);
return sema.analyzeIsErr(block, src, loaded);
return sema.analyzeIsNonErr(block, src, loaded);
}
fn zirCondbr(
@@ -7219,20 +7219,25 @@ fn analyzeIsNull(
return block.addUnOp(src, result_ty, inst_tag, operand);
}
fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
fn analyzeIsNonErr(
sema: *Sema,
block: *Scope.Block,
src: LazySrcLoc,
operand: *Inst,
) InnerError!*Inst {
const ot = operand.ty.zigTypeTag();
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true);
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false);
assert(ot == .ErrorUnion);
const result_ty = Type.initTag(.bool);
if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
if (err_union.isUndef()) {
return sema.mod.constUndef(sema.arena, src, result_ty);
}
return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
return sema.mod.constBool(sema.arena, src, err_union.getError() == null);
}
try sema.requireRuntimeBlock(block, src);
return block.addUnOp(src, result_ty, .is_err, operand);
return block.addUnOp(src, result_ty, .is_non_err, operand);
}
fn analyzeSlice(

View File

@@ -398,21 +398,15 @@ pub const Inst = struct {
/// Return a boolean false if an optional is null. `x != null`
/// Uses the `un_node` field.
is_non_null,
/// Return a boolean true if an optional is null. `x == null`
/// Uses the `un_node` field.
is_null,
/// Return a boolean false if an optional is null. `x.* != null`
/// Uses the `un_node` field.
is_non_null_ptr,
/// Return a boolean true if an optional is null. `x.* == null`
/// Return a boolean false if value is an error
/// Uses the `un_node` field.
is_null_ptr,
/// Return a boolean true if value is an error
is_non_err,
/// Return a boolean false if dereferenced pointer is an error
/// Uses the `un_node` field.
is_err,
/// Return a boolean true if dereferenced pointer is an error
/// Uses the `un_node` field.
is_err_ptr,
is_non_err_ptr,
/// A labeled block of code that loops forever. At the end of the body will have either
/// a `repeat` instruction or a `repeat_inline` instruction.
/// Uses the `pl_node` field. The AST node is either a for loop or while loop.
@@ -1046,11 +1040,9 @@ pub const Inst = struct {
.float128,
.int_type,
.is_non_null,
.is_null,
.is_non_null_ptr,
.is_null_ptr,
.is_err,
.is_err_ptr,
.is_non_err,
.is_non_err_ptr,
.mod_rem,
.mul,
.mulwrap,
@@ -1306,11 +1298,9 @@ pub const Inst = struct {
.float128 = .pl_node,
.int_type = .int_type,
.is_non_null = .un_node,
.is_null = .un_node,
.is_non_null_ptr = .un_node,
.is_null_ptr = .un_node,
.is_err = .un_node,
.is_err_ptr = .un_node,
.is_non_err = .un_node,
.is_non_err_ptr = .un_node,
.loop = .pl_node,
.repeat = .node,
.repeat_inline = .node,
@@ -2857,11 +2847,9 @@ const Writer = struct {
.err_union_code,
.err_union_code_ptr,
.is_non_null,
.is_null,
.is_non_null_ptr,
.is_null_ptr,
.is_err,
.is_err_ptr,
.is_non_err,
.is_non_err_ptr,
.typeof,
.typeof_elem,
.struct_init_empty,

View File

@@ -90,8 +90,12 @@ pub const Inst = struct {
is_non_null_ptr,
/// E!T => bool
is_err,
/// E!T => bool (inverted logic)
is_non_err,
/// *E!T => bool
is_err_ptr,
/// *E!T => bool (inverted logic)
is_non_err_ptr,
bool_and,
bool_or,
/// Read a value from a pointer.
@@ -154,7 +158,9 @@ pub const Inst = struct {
.is_null,
.is_null_ptr,
.is_err,
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.ptrtoint,
.floatcast,
.intcast,
@@ -759,7 +765,9 @@ const DumpAir = struct {
.is_null,
.is_null_ptr,
.is_err,
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.ptrtoint,
.floatcast,
.intcast,
@@ -888,11 +896,13 @@ const DumpAir = struct {
.bitcast,
.not,
.is_non_null,
.is_null,
.is_non_null_ptr,
.is_null,
.is_null_ptr,
.is_err,
.is_err_ptr,
.is_non_err,
.is_non_err_ptr,
.ptrtoint,
.floatcast,
.intcast,

View File

@@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
.is_null => return self.genIsNull(inst.castTag(.is_null).?),
.is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
.is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),
.is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),
.is_err => return self.genIsErr(inst.castTag(.is_err).?),
.is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
.load => return self.genLoad(inst.castTag(.load).?),
@@ -2972,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
}
fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
switch (arch) {
else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}),
}
}
fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{});
}
fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
// A loop is a setup to be able to jump back to the beginning.
const start_index = self.code.items.len;

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 };
}