From 5c8bd443d92c6306f60857720103ae46ca7b8b3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Jul 2021 19:50:56 -0700 Subject: [PATCH] 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. --- src/AstGen.zig | 92 ++++++++++++++++++++++---------------------- src/Sema.zig | 55 ++++++++++++++------------ src/Zir.zig | 32 +++++---------- src/air.zig | 12 +++++- src/codegen.zig | 12 ++++++ src/codegen/c.zig | 11 +++--- src/codegen/wasm.zig | 9 ++--- 7 files changed, 119 insertions(+), 104 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index f7fea3a99c..a816628f66 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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; }, diff --git a/src/Sema.zig b/src/Sema.zig index 24c51bdc46..86e5f59af6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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( diff --git a/src/Zir.zig b/src/Zir.zig index a80660b5bf..77b71f6caf 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -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, diff --git a/src/air.zig b/src/air.zig index 1b7faa641b..e73367945b 100644 --- a/src/air.zig +++ b/src/air.zig @@ -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, diff --git a/src/codegen.zig b/src/codegen.zig index f9f5a861fb..6b9bd633d0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a9521d21a8..db0e910643 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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; } diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index ec4ec66b1e..3476ab2ce6 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -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 }; }