Commit 5c8bd443d9

Andrew Kelley <andrew@ziglang.org>
2021-07-08 04:50:56
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.
1 parent 5816997
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;
 }
 
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 };
     }
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,
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,21 +4638,21 @@ 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);
-
-    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) {
+    const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
+    const then_result = switch (rl) {
         .ref => unwrapped_payload,
-        else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
+        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);
+
+    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,
         rl,
@@ -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;
         },
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;
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(
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`
-        /// Uses the `un_node` field.
-        is_null_ptr,
-        /// Return a boolean true if value is an error
+        /// Return a boolean false if value is an error
         /// Uses the `un_node` field.
-        is_err,
-        /// Return a boolean true if dereferenced pointer is an error
+        is_non_err,
+        /// Return a boolean false 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,