Commit 0333ff4476

Veikka Tuominen <git@vexu.eu>
2022-06-10 13:11:59
stage2: make `error{}` the same size as `anyerror`
Having `error{}` be a zero bit type causes issues when it interracts with empty inferred error sets which are the same size as `anyerror`.
1 parent 488e1e5
Changed files (11)
lib
std
compress
src
test
behavior
lib/std/compress/deflate/compressor_test.zig
@@ -179,7 +179,6 @@ test "deflate/inflate" {
 }
 
 test "very long sparse chunk" {
-    if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest;
     // A SparseReader returns a stream consisting of 0s ending with 65,536 (1<<16) 1s.
     // This tests missing hash references in a very large input.
     const SparseReader = struct {
@@ -377,7 +376,6 @@ test "compressor dictionary" {
 // Update the hash for best_speed only if d.index < d.maxInsertIndex
 // See https://golang.org/issue/2508
 test "Go non-regression test for 2508" {
-    if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest;
     var comp = try compressor(
         testing.allocator,
         io.null_writer,
src/arch/aarch64/CodeGen.zig
@@ -2277,7 +2277,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
 fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
     const err_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
-    if (err_ty.errorSetCardinality() == .zero) {
+    if (err_ty.errorSetIsEmpty()) {
         return MCValue{ .immediate = 0 };
     }
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
@@ -2311,7 +2311,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
 fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
     const err_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
-    if (err_ty.errorSetCardinality() == .zero) {
+    if (err_ty.errorSetIsEmpty()) {
         return error_union_mcv;
     }
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
@@ -3590,7 +3590,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
     const error_type = ty.errorUnionSet();
     const payload_type = ty.errorUnionPayload();
 
-    if (error_type.errorSetCardinality() == .zero) {
+    if (error_type.errorSetIsEmpty()) {
         return MCValue{ .immediate = 0 }; // always false
     }
 
@@ -4687,11 +4687,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
             const error_type = typed_value.ty.errorUnionSet();
             const payload_type = typed_value.ty.errorUnionPayload();
 
-            if (error_type.errorSetCardinality() == .zero) {
-                const payload_val = typed_value.val.castTag(.eu_payload).?.data;
-                return self.genTypedValue(.{ .ty = payload_type, .val = payload_val });
-            }
-
             const is_pl = typed_value.val.errorUnionIsPayload();
 
             if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
src/arch/arm/CodeGen.zig
@@ -1773,7 +1773,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
 fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
     const err_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
-    if (err_ty.errorSetCardinality() == .zero) {
+    if (err_ty.errorSetIsEmpty()) {
         return MCValue{ .immediate = 0 };
     }
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
@@ -1810,7 +1810,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
 fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
     const err_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
-    if (err_ty.errorSetCardinality() == .zero) {
+    if (err_ty.errorSetIsEmpty()) {
         return error_union_mcv;
     }
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
@@ -3922,7 +3922,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
     const error_type = ty.errorUnionSet();
     const error_int_type = Type.initTag(.u16);
 
-    if (error_type.errorSetCardinality() == .zero) {
+    if (error_type.errorSetIsEmpty()) {
         return MCValue{ .immediate = 0 }; // always false
     }
 
@@ -5368,12 +5368,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         .ErrorUnion => {
             const error_type = typed_value.ty.errorUnionSet();
             const payload_type = typed_value.ty.errorUnionPayload();
-
-            if (error_type.errorSetCardinality() == .zero) {
-                const payload_val = typed_value.val.castTag(.eu_payload).?.data;
-                return self.genTypedValue(.{ .ty = payload_type, .val = payload_val });
-            }
-
             const is_pl = typed_value.val.errorUnionIsPayload();
 
             if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
src/arch/wasm/CodeGen.zig
@@ -1377,11 +1377,7 @@ fn isByRef(ty: Type, target: std.Target) bool {
         .Int => return ty.intInfo(target).bits > 64,
         .Float => return ty.floatBits(target) > 64,
         .ErrorUnion => {
-            const err_ty = ty.errorUnionSet();
             const pl_ty = ty.errorUnionPayload();
-            if (err_ty.errorSetCardinality() == .zero) {
-                return isByRef(pl_ty, target);
-            }
             if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
                 return false;
             }
@@ -1816,11 +1812,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
 fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void {
     switch (ty.zigTypeTag()) {
         .ErrorUnion => {
-            const err_ty = ty.errorUnionSet();
             const pl_ty = ty.errorUnionPayload();
-            if (err_ty.errorSetCardinality() == .zero) {
-                return self.store(lhs, rhs, pl_ty, 0);
-            }
             if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
                 return self.store(lhs, rhs, Type.anyerror, 0);
             }
@@ -2353,10 +2345,6 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
         },
         .ErrorUnion => {
             const error_type = ty.errorUnionSet();
-            if (error_type.errorSetCardinality() == .zero) {
-                const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
-                return self.lowerConstant(pl_val, ty.errorUnionPayload());
-            }
             const is_pl = val.errorUnionIsPayload();
             const err_val = if (!is_pl) val else Value.initTag(.zero);
             return self.lowerConstant(err_val, error_type);
@@ -2925,7 +2913,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
     const err_union_ty = self.air.typeOf(un_op);
     const pl_ty = err_union_ty.errorUnionPayload();
 
-    if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
+    if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
         switch (opcode) {
             .i32_ne => return WValue{ .imm32 = 0 },
             .i32_eq => return WValue{ .imm32 = 1 },
@@ -2958,10 +2946,6 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool)
     const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
     const payload_ty = err_ty.errorUnionPayload();
 
-    if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
-        return operand;
-    }
-
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
 
     const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target));
@@ -2980,7 +2964,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
     const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
     const payload_ty = err_ty.errorUnionPayload();
 
-    if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
+    if (err_ty.errorUnionSet().errorSetIsEmpty()) {
         return WValue{ .imm32 = 0 };
     }
 
@@ -2998,10 +2982,6 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     const operand = try self.resolveInst(ty_op.operand);
     const err_ty = self.air.typeOfIndex(inst);
 
-    if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
-        return operand;
-    }
-
     const pl_ty = self.air.typeOf(ty_op.operand);
     if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
         return operand;
@@ -4656,29 +4636,27 @@ fn lowerTry(
         return self.fail("TODO: lowerTry for pointers", .{});
     }
 
-    if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
-        return err_union;
-    }
-
     const pl_ty = err_union_ty.errorUnionPayload();
     const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime();
 
-    // Block we can jump out of when error is not set
-    try self.startBlock(.block, wasm.block_empty);
-
-    // check if the error tag is set for the error union.
-    try self.emitWValue(err_union);
-    if (pl_has_bits) {
-        const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
-        try self.addMemArg(.i32_load16_u, .{
-            .offset = err_union.offset() + err_offset,
-            .alignment = Type.anyerror.abiAlignment(self.target),
-        });
+    if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {
+        // Block we can jump out of when error is not set
+        try self.startBlock(.block, wasm.block_empty);
+
+        // check if the error tag is set for the error union.
+        try self.emitWValue(err_union);
+        if (pl_has_bits) {
+            const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
+            try self.addMemArg(.i32_load16_u, .{
+                .offset = err_union.offset() + err_offset,
+                .alignment = Type.anyerror.abiAlignment(self.target),
+            });
+        }
+        try self.addTag(.i32_eqz);
+        try self.addLabel(.br_if, 0); // jump out of block when error is '0'
+        try self.genBody(body);
+        try self.endBlock();
     }
-    try self.addTag(.i32_eqz);
-    try self.addLabel(.br_if, 0); // jump out of block when error is '0'
-    try self.genBody(body);
-    try self.endBlock();
 
     // if we reach here it means error was not set, and we want the payload
     if (!pl_has_bits) {
src/arch/x86_64/CodeGen.zig
@@ -1806,7 +1806,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
     const operand = try self.resolveInst(ty_op.operand);
 
     const result: MCValue = result: {
-        if (err_ty.errorSetCardinality() == .zero) {
+        if (err_ty.errorSetIsEmpty()) {
             break :result MCValue{ .immediate = 0 };
         }
 
@@ -1857,14 +1857,8 @@ fn genUnwrapErrorUnionPayloadMir(
     err_union: MCValue,
 ) !MCValue {
     const payload_ty = err_union_ty.errorUnionPayload();
-    const err_ty = err_union_ty.errorUnionSet();
 
     const result: MCValue = result: {
-        if (err_ty.errorSetCardinality() == .zero) {
-            // TODO check if we can reuse
-            break :result err_union;
-        }
-
         if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
             break :result MCValue.none;
         }
@@ -1991,15 +1985,10 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
     }
 
     const error_union_ty = self.air.getRefType(ty_op.ty);
-    const error_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
     const operand = try self.resolveInst(ty_op.operand);
 
     const result: MCValue = result: {
-        if (error_ty.errorSetCardinality() == .zero) {
-            break :result operand;
-        }
-
         if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
             break :result operand;
         }
@@ -4651,7 +4640,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV
 fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
     const err_type = ty.errorUnionSet();
 
-    if (err_type.errorSetCardinality() == .zero) {
+    if (err_type.errorSetIsEmpty()) {
         return MCValue{ .immediate = 0 }; // always false
     }
 
@@ -6909,12 +6898,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         .ErrorUnion => {
             const error_type = typed_value.ty.errorUnionSet();
             const payload_type = typed_value.ty.errorUnionPayload();
-
-            if (error_type.errorSetCardinality() == .zero) {
-                const payload_val = typed_value.val.castTag(.eu_payload).?.data;
-                return self.genTypedValue(.{ .ty = payload_type, .val = payload_val });
-            }
-
             const is_pl = typed_value.val.errorUnionIsPayload();
 
             if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
src/codegen/c.zig
@@ -752,12 +752,6 @@ pub const DeclGen = struct {
                 const error_type = ty.errorUnionSet();
                 const payload_type = ty.errorUnionPayload();
 
-                if (error_type.errorSetCardinality() == .zero) {
-                    // We use the payload directly as the type.
-                    const payload_val = val.castTag(.eu_payload).?.data;
-                    return dg.renderValue(writer, payload_type, payload_val, location);
-                }
-
                 if (!payload_type.hasRuntimeBits()) {
                     // We use the error type directly as the type.
                     const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val;
@@ -1381,13 +1375,8 @@ pub const DeclGen = struct {
                 return w.writeAll("uint16_t");
             },
             .ErrorUnion => {
-                const error_ty = t.errorUnionSet();
                 const payload_ty = t.errorUnionPayload();
 
-                if (error_ty.errorSetCardinality() == .zero) {
-                    return dg.renderType(w, payload_ty);
-                }
-
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
                     return dg.renderType(w, Type.anyerror);
                 }
@@ -2892,41 +2881,36 @@ fn lowerTry(
     operand_is_ptr: bool,
     result_ty: Type,
 ) !CValue {
-    if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
-        // If the error set has no fields, then the payload and the error
-        // union are the same value.
-        return err_union;
-    }
-
+    const writer = f.object.writer();
     const payload_ty = err_union_ty.errorUnionPayload();
     const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
 
-    const writer = f.object.writer();
-
-    err: {
-        if (!payload_has_bits) {
-            if (operand_is_ptr) {
-                try writer.writeAll("if(*");
-            } else {
+    if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {
+        err: {
+            if (!payload_has_bits) {
+                if (operand_is_ptr) {
+                    try writer.writeAll("if(*");
+                } else {
+                    try writer.writeAll("if(");
+                }
+                try f.writeCValue(writer, err_union);
+                try writer.writeAll(")");
+                break :err;
+            }
+            if (operand_is_ptr or isByRef(err_union_ty)) {
                 try writer.writeAll("if(");
+                try f.writeCValue(writer, err_union);
+                try writer.writeAll("->error)");
+                break :err;
             }
-            try f.writeCValue(writer, err_union);
-            try writer.writeAll(")");
-            break :err;
-        }
-        if (operand_is_ptr or isByRef(err_union_ty)) {
             try writer.writeAll("if(");
             try f.writeCValue(writer, err_union);
-            try writer.writeAll("->error)");
-            break :err;
+            try writer.writeAll(".error)");
         }
-        try writer.writeAll("if(");
-        try f.writeCValue(writer, err_union);
-        try writer.writeAll(".error)");
-    }
 
-    try genBody(f, body);
-    try f.object.indent_writer.insertNewline();
+        try genBody(f, body);
+        try f.object.indent_writer.insertNewline();
+    }
 
     if (!payload_has_bits) {
         if (!operand_is_ptr) {
@@ -3466,7 +3450,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
 
     if (operand_ty.zigTypeTag() == .Pointer) {
         const err_union_ty = operand_ty.childType();
-        if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
+        if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
             return CValue{ .bytes = "0" };
         }
         if (!err_union_ty.errorUnionPayload().hasRuntimeBits()) {
@@ -3478,7 +3462,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
         try writer.writeAll(";\n");
         return local;
     }
-    if (operand_ty.errorUnionSet().errorSetCardinality() == .zero) {
+    if (operand_ty.errorUnionSet().errorSetIsEmpty()) {
         return CValue{ .bytes = "0" };
     }
     if (!operand_ty.errorUnionPayload().hasRuntimeBits()) {
@@ -3507,10 +3491,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: [*:0]c
     const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer;
     const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
 
-    if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
-        return operand;
-    }
-
     if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) {
         return CValue.none;
     }
@@ -3575,11 +3555,6 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
     const error_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
 
-    if (error_ty.errorSetCardinality() == .zero) {
-        // TODO: write undefined bytes through the pointer here
-        return operand;
-    }
-
     // First, set the non-error value.
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
         try f.writeCValueDeref(writer, operand);
@@ -3623,9 +3598,6 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
     const operand = try f.resolveInst(ty_op.operand);
 
     const inst_ty = f.air.typeOfIndex(inst);
-    if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) {
-        return operand;
-    }
     const local = try f.allocLocal(inst_ty, .Const);
     try writer.writeAll(" = { .error = 0, .payload = ");
     try f.writeCValue(writer, operand);
@@ -3652,7 +3624,7 @@ fn airIsErr(
 
     try writer.writeAll(" = ");
 
-    if (error_ty.errorSetCardinality() == .zero) {
+    if (error_ty.errorSetIsEmpty()) {
         try writer.print("0 {s} 0;\n", .{op_str});
     } else {
         if (is_ptr) {
src/codegen/llvm.zig
@@ -1571,22 +1571,6 @@ pub const Object = struct {
             },
             .ErrorUnion => {
                 const payload_ty = ty.errorUnionPayload();
-                switch (ty.errorUnionSet().errorSetCardinality()) {
-                    .zero => {
-                        const payload_di_ty = try o.lowerDebugType(payload_ty, .full);
-                        // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                        try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module });
-                        return payload_di_ty;
-                    },
-                    .one => {
-                        if (payload_ty.isNoReturn()) {
-                            const di_type = dib.createBasicType("void", 0, DW.ATE.signed);
-                            gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type);
-                            return di_type;
-                        }
-                    },
-                    .many => {},
-                }
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
                     const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
@@ -2554,15 +2538,6 @@ pub const DeclGen = struct {
             },
             .ErrorUnion => {
                 const payload_ty = t.errorUnionPayload();
-                switch (t.errorUnionSet().errorSetCardinality()) {
-                    .zero => return dg.lowerType(payload_ty),
-                    .one => {
-                        if (payload_ty.isNoReturn()) {
-                            return dg.context.voidType();
-                        }
-                    },
-                    .many => {},
-                }
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
                     return try dg.lowerType(Type.anyerror);
                 }
@@ -3222,10 +3197,6 @@ pub const DeclGen = struct {
             },
             .ErrorUnion => {
                 const payload_type = tv.ty.errorUnionPayload();
-                if (tv.ty.errorUnionSet().errorSetCardinality() == .zero) {
-                    const payload_val = tv.val.castTag(.eu_payload).?.data;
-                    return dg.lowerValue(.{ .ty = payload_type, .val = payload_val });
-                }
                 const is_pl = tv.val.errorUnionIsPayload();
 
                 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
@@ -4795,40 +4766,37 @@ pub const FuncGen = struct {
     }
 
     fn lowerTry(fg: *FuncGen, err_union: *const llvm.Value, body: []const Air.Inst.Index, err_union_ty: Type, operand_is_ptr: bool, result_ty: Type) !?*const llvm.Value {
-        if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
-            // If the error set has no fields, then the payload and the error
-            // union are the same value.
-            return err_union;
-        }
-
         const payload_ty = err_union_ty.errorUnionPayload();
         const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
         const target = fg.dg.module.getTarget();
-        const is_err = err: {
-            const err_set_ty = try fg.dg.lowerType(Type.anyerror);
-            const zero = err_set_ty.constNull();
-            if (!payload_has_bits) {
-                const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union;
-                break :err fg.builder.buildICmp(.NE, loaded, zero, "");
-            }
-            const err_field_index = errUnionErrorOffset(payload_ty, target);
-            if (operand_is_ptr or isByRef(err_union_ty)) {
-                const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, "");
-                const loaded = fg.builder.buildLoad(err_field_ptr, "");
+
+        if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) {
+            const is_err = err: {
+                const err_set_ty = try fg.dg.lowerType(Type.anyerror);
+                const zero = err_set_ty.constNull();
+                if (!payload_has_bits) {
+                    const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union;
+                    break :err fg.builder.buildICmp(.NE, loaded, zero, "");
+                }
+                const err_field_index = errUnionErrorOffset(payload_ty, target);
+                if (operand_is_ptr or isByRef(err_union_ty)) {
+                    const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, "");
+                    const loaded = fg.builder.buildLoad(err_field_ptr, "");
+                    break :err fg.builder.buildICmp(.NE, loaded, zero, "");
+                }
+                const loaded = fg.builder.buildExtractValue(err_union, err_field_index, "");
                 break :err fg.builder.buildICmp(.NE, loaded, zero, "");
-            }
-            const loaded = fg.builder.buildExtractValue(err_union, err_field_index, "");
-            break :err fg.builder.buildICmp(.NE, loaded, zero, "");
-        };
+            };
 
-        const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet");
-        const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont");
-        _ = fg.builder.buildCondBr(is_err, return_block, continue_block);
+            const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet");
+            const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont");
+            _ = fg.builder.buildCondBr(is_err, return_block, continue_block);
 
-        fg.builder.positionBuilderAtEnd(return_block);
-        try fg.genBody(body);
+            fg.builder.positionBuilderAtEnd(return_block);
+            try fg.genBody(body);
 
-        fg.builder.positionBuilderAtEnd(continue_block);
+            fg.builder.positionBuilderAtEnd(continue_block);
+        }
         if (!payload_has_bits) {
             if (!operand_is_ptr) return null;
 
@@ -5665,7 +5633,7 @@ pub const FuncGen = struct {
         const err_set_ty = try self.dg.lowerType(Type.initTag(.anyerror));
         const zero = err_set_ty.constNull();
 
-        if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
+        if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
             const llvm_i1 = self.context.intType(1);
             switch (op) {
                 .EQ => return llvm_i1.constInt(1, .False), // 0 == 0
@@ -5788,13 +5756,6 @@ pub const FuncGen = struct {
 
         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
         const operand = try self.resolveInst(ty_op.operand);
-        const operand_ty = self.air.typeOf(ty_op.operand);
-        const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
-        if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
-            // If the error set has no fields, then the payload and the error
-            // union are the same value.
-            return operand;
-        }
         const result_ty = self.air.typeOfIndex(inst);
         const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
         const target = self.dg.module.getTarget();
@@ -5825,7 +5786,7 @@ pub const FuncGen = struct {
         const operand = try self.resolveInst(ty_op.operand);
         const operand_ty = self.air.typeOf(ty_op.operand);
         const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
-        if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
+        if (err_union_ty.errorUnionSet().errorSetIsEmpty()) {
             const err_llvm_ty = try self.dg.lowerType(Type.anyerror);
             if (operand_is_ptr) {
                 return self.builder.buildBitCast(operand, err_llvm_ty.pointerType(0), "");
@@ -5856,10 +5817,6 @@ pub const FuncGen = struct {
         const operand = try self.resolveInst(ty_op.operand);
         const error_union_ty = self.air.typeOf(ty_op.operand).childType();
 
-        if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
-            // TODO: write undefined bytes through the pointer here
-            return operand;
-        }
         const payload_ty = error_union_ty.errorUnionPayload();
         const non_error_val = try self.dg.lowerValue(.{ .ty = Type.anyerror, .val = Value.zero });
         if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
@@ -5938,9 +5895,6 @@ pub const FuncGen = struct {
         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
         const inst_ty = self.air.typeOfIndex(inst);
         const operand = try self.resolveInst(ty_op.operand);
-        if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) {
-            return operand;
-        }
         const payload_ty = self.air.typeOf(ty_op.operand);
         if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
             return operand;
src/codegen.zig
@@ -705,15 +705,6 @@ pub fn generateSymbol(
         .ErrorUnion => {
             const error_ty = typed_value.ty.errorUnionSet();
             const payload_ty = typed_value.ty.errorUnionPayload();
-
-            if (error_ty.errorSetCardinality() == .zero) {
-                const payload_val = typed_value.val.castTag(.eu_payload).?.data;
-                return generateSymbol(bin_file, src_loc, .{
-                    .ty = payload_ty,
-                    .val = payload_val,
-                }, code, debug_output, reloc_info);
-            }
-
             const is_payload = typed_value.val.errorUnionIsPayload();
 
             if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
src/Sema.zig
@@ -6182,6 +6182,17 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
         }
     }
 
+    const op_ty = sema.typeOf(op);
+    try sema.resolveInferredErrorSetTy(block, src, op_ty);
+    if (!op_ty.isAnyError()) {
+        const names = op_ty.errorSetNames();
+        switch (names.len) {
+            0 => return sema.addConstant(result_ty, Value.zero),
+            1 => return sema.addIntUnsigned(result_ty, sema.mod.global_error_set.get(names[0]).?),
+            else => {},
+        }
+    }
+
     try sema.requireRuntimeBlock(block, src);
     return block.addBitCast(result_ty, op_coerced);
 }
@@ -6560,7 +6571,7 @@ fn analyzeErrUnionPayload(
 
     // If the error set has no fields then no safety check is needed.
     if (safety_check and block.wantSafety() and
-        err_union_ty.errorUnionSet().errorSetCardinality() != .zero)
+        !err_union_ty.errorUnionSet().errorSetIsEmpty())
     {
         try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
     }
@@ -6646,7 +6657,7 @@ fn analyzeErrUnionPayloadPtr(
 
     // If the error set has no fields then no safety check is needed.
     if (safety_check and block.wantSafety() and
-        err_union_ty.errorUnionSet().errorSetCardinality() != .zero)
+        !err_union_ty.errorUnionSet().errorSetIsEmpty())
     {
         try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
     }
@@ -24231,6 +24242,10 @@ pub fn typeHasOnePossibleValue(
         .bool,
         .type,
         .anyerror,
+        .error_set_single,
+        .error_set,
+        .error_set_merged,
+        .error_union,
         .fn_noreturn_no_args,
         .fn_void_no_args,
         .fn_naked_noreturn_no_args,
@@ -24287,46 +24302,6 @@ pub fn typeHasOnePossibleValue(
             }
         },
 
-        .error_union => {
-            const error_ty = ty.errorUnionSet();
-            switch (error_ty.errorSetCardinality()) {
-                .zero => {
-                    const payload_ty = ty.errorUnionPayload();
-                    if (try typeHasOnePossibleValue(sema, block, src, payload_ty)) |payload_val| {
-                        return try Value.Tag.eu_payload.create(sema.arena, payload_val);
-                    } else {
-                        return null;
-                    }
-                },
-                .one => {
-                    if (ty.errorUnionPayload().isNoReturn()) {
-                        const error_val = (try typeHasOnePossibleValue(sema, block, src, error_ty)).?;
-                        return error_val;
-                    } else {
-                        return null;
-                    }
-                },
-                .many => return null,
-            }
-        },
-
-        .error_set_single => {
-            const name = ty.castTag(.error_set_single).?.data;
-            return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
-        },
-        .error_set => {
-            const err_set_obj = ty.castTag(.error_set).?.data;
-            const names = err_set_obj.names.keys();
-            if (names.len > 1) return null;
-            return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] });
-        },
-        .error_set_merged => {
-            const name_map = ty.castTag(.error_set_merged).?.data;
-            const names = name_map.keys();
-            if (names.len > 1) return null;
-            return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] });
-        },
-
         .@"struct" => {
             const resolved_ty = try sema.resolveTypeFields(block, src, ty);
             const s = resolved_ty.castTag(.@"struct").?.data;
src/type.zig
@@ -2366,6 +2366,10 @@ pub const Type = extern union {
             .anyopaque,
             .@"opaque",
             .type_info,
+            .error_set_single,
+            .error_union,
+            .error_set,
+            .error_set_merged,
             => return true,
 
             // These are false because they are comptime-only types.
@@ -2389,20 +2393,8 @@ pub const Type = extern union {
             .fn_void_no_args,
             .fn_naked_noreturn_no_args,
             .fn_ccc_void_no_args,
-            .error_set_single,
             => return false,
 
-            .error_set => {
-                const err_set_obj = ty.castTag(.error_set).?.data;
-                const names = err_set_obj.names.keys();
-                return names.len > 1;
-            },
-            .error_set_merged => {
-                const name_map = ty.castTag(.error_set_merged).?.data;
-                const names = name_map.keys();
-                return names.len > 1;
-            },
-
             // These types have more than one possible value, so the result is the same as
             // asking whether they are comptime-only types.
             .anyframe_T,
@@ -2443,25 +2435,6 @@ pub const Type = extern union {
                 }
             },
 
-            .error_union => {
-                // This code needs to be kept in sync with the equivalent switch prong
-                // in abiSizeAdvanced.
-                const data = ty.castTag(.error_union).?.data;
-                switch (data.error_set.errorSetCardinality()) {
-                    .zero => return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit),
-                    .one => return !data.payload.isNoReturn(),
-                    .many => {
-                        if (ignore_comptime_only) {
-                            return true;
-                        } else if (sema_kit) |sk| {
-                            return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
-                        } else {
-                            return !comptimeOnly(ty);
-                        }
-                    },
-                }
-            },
-
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
                 if (struct_obj.status == .field_types_wip) {
@@ -2926,27 +2899,11 @@ pub const Type = extern union {
             .anyerror_void_error_union,
             .anyerror,
             .error_set_inferred,
+            .error_set_single,
+            .error_set,
+            .error_set_merged,
             => return AbiAlignmentAdvanced{ .scalar = 2 },
 
-            .error_set => {
-                const err_set_obj = ty.castTag(.error_set).?.data;
-                const names = err_set_obj.names.keys();
-                if (names.len <= 1) {
-                    return AbiAlignmentAdvanced{ .scalar = 0 };
-                } else {
-                    return AbiAlignmentAdvanced{ .scalar = 2 };
-                }
-            },
-            .error_set_merged => {
-                const name_map = ty.castTag(.error_set_merged).?.data;
-                const names = name_map.keys();
-                if (names.len <= 1) {
-                    return AbiAlignmentAdvanced{ .scalar = 0 };
-                } else {
-                    return AbiAlignmentAdvanced{ .scalar = 2 };
-                }
-            },
-
             .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat),
 
             // TODO audit this - is there any more complicated logic to determine
@@ -2971,12 +2928,7 @@ pub const Type = extern union {
 
                 switch (child_type.zigTypeTag()) {
                     .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
-                    .ErrorSet => switch (child_type.errorSetCardinality()) {
-                        // `?error{}` is comptime-known to be null.
-                        .zero => return AbiAlignmentAdvanced{ .scalar = 0 },
-                        .one => return AbiAlignmentAdvanced{ .scalar = 1 },
-                        .many => return abiAlignmentAdvanced(Type.anyerror, target, strat),
-                    },
+                    .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat),
                     .NoReturn => return AbiAlignmentAdvanced{ .scalar = 0 },
                     else => {},
                 }
@@ -2999,15 +2951,6 @@ pub const Type = extern union {
                 // This code needs to be kept in sync with the equivalent switch prong
                 // in abiSizeAdvanced.
                 const data = ty.castTag(.error_union).?.data;
-                switch (data.error_set.errorSetCardinality()) {
-                    .zero => return abiAlignmentAdvanced(data.payload, target, strat),
-                    .one => {
-                        if (data.payload.isNoReturn()) {
-                            return AbiAlignmentAdvanced{ .scalar = 0 };
-                        }
-                    },
-                    .many => {},
-                }
                 const code_align = abiAlignment(Type.anyerror, target);
                 switch (strat) {
                     .eager, .sema_kit => {
@@ -3118,7 +3061,6 @@ pub const Type = extern union {
             .@"undefined",
             .enum_literal,
             .type_info,
-            .error_set_single,
             => return AbiAlignmentAdvanced{ .scalar = 0 },
 
             .noreturn,
@@ -3237,7 +3179,6 @@ pub const Type = extern union {
             .empty_struct_literal,
             .empty_struct,
             .void,
-            .error_set_single,
             => return AbiSizeAdvanced{ .scalar = 0 },
 
             .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) {
@@ -3396,27 +3337,11 @@ pub const Type = extern union {
             .anyerror_void_error_union,
             .anyerror,
             .error_set_inferred,
+            .error_set,
+            .error_set_merged,
+            .error_set_single,
             => return AbiSizeAdvanced{ .scalar = 2 },
 
-            .error_set => {
-                const err_set_obj = ty.castTag(.error_set).?.data;
-                const names = err_set_obj.names.keys();
-                if (names.len <= 1) {
-                    return AbiSizeAdvanced{ .scalar = 0 };
-                } else {
-                    return AbiSizeAdvanced{ .scalar = 2 };
-                }
-            },
-            .error_set_merged => {
-                const name_map = ty.castTag(.error_set_merged).?.data;
-                const names = name_map.keys();
-                if (names.len <= 1) {
-                    return AbiSizeAdvanced{ .scalar = 0 };
-                } else {
-                    return AbiSizeAdvanced{ .scalar = 2 };
-                }
-            },
-
             .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) },
             .u29 => return AbiSizeAdvanced{ .scalar = intAbiSize(29, target) },
             .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) },
@@ -3467,24 +3392,6 @@ pub const Type = extern union {
                 // This code needs to be kept in sync with the equivalent switch prong
                 // in abiAlignmentAdvanced.
                 const data = ty.castTag(.error_union).?.data;
-                // Here we need to care whether or not the error set is *empty* or whether
-                // it only has *one possible value*. In the former case, it means there
-                // cannot possibly be an error, meaning the ABI size is equivalent to the
-                // payload ABI size. In the latter case, we need to account for the "tag"
-                // because even if both the payload type and the error set type of an
-                // error union have no runtime bits, an error union still has
-                // 1 bit of data which is whether or not the value is an error.
-                // Zig still uses the error code encoding at runtime, even when only 1 bit
-                // would suffice. This prevents coercions from needing to branch.
-                switch (data.error_set.errorSetCardinality()) {
-                    .zero => return abiSizeAdvanced(data.payload, target, strat),
-                    .one => {
-                        if (data.payload.isNoReturn()) {
-                            return AbiSizeAdvanced{ .scalar = 0 };
-                        }
-                    },
-                    .many => {},
-                }
                 const code_size = abiSize(Type.anyerror, target);
                 if (!data.payload.hasRuntimeBits()) {
                     // Same as anyerror.
@@ -3727,11 +3634,7 @@ pub const Type = extern union {
 
             .error_union => {
                 const payload = ty.castTag(.error_union).?.data;
-                if (!payload.error_set.hasRuntimeBits() and !payload.payload.hasRuntimeBits()) {
-                    return 0;
-                } else if (!payload.error_set.hasRuntimeBits()) {
-                    return payload.payload.bitSizeAdvanced(target, sema_kit);
-                } else if (!payload.payload.hasRuntimeBits()) {
+                if (!payload.payload.hasRuntimeBits()) {
                     return payload.error_set.bitSizeAdvanced(target, sema_kit);
                 }
                 @panic("TODO bitSize error union");
@@ -4351,30 +4254,25 @@ pub const Type = extern union {
         };
     }
 
-    const ErrorSetCardinality = enum { zero, one, many };
-
-    pub fn errorSetCardinality(ty: Type) ErrorSetCardinality {
+    /// Returns false for unresolved inferred error sets.
+    pub fn errorSetIsEmpty(ty: Type) bool {
         switch (ty.tag()) {
-            .anyerror => return .many,
-            .error_set_inferred => return .many,
-            .error_set_single => return .one,
+            .anyerror => return false,
+            .error_set_inferred => {
+                const inferred_error_set = ty.castTag(.error_set_inferred).?.data;
+                // Can't know for sure.
+                if (!inferred_error_set.is_resolved) return false;
+                if (inferred_error_set.is_anyerror) return false;
+                return inferred_error_set.errors.count() == 0;
+            },
+            .error_set_single => return false,
             .error_set => {
                 const err_set_obj = ty.castTag(.error_set).?.data;
-                const names = err_set_obj.names.keys();
-                switch (names.len) {
-                    0 => return .zero,
-                    1 => return .one,
-                    else => return .many,
-                }
+                return err_set_obj.names.count() == 0;
             },
             .error_set_merged => {
                 const name_map = ty.castTag(.error_set_merged).?.data;
-                const names = name_map.keys();
-                switch (names.len) {
-                    0 => return .zero,
-                    1 => return .one,
-                    else => return .many,
-                }
+                return name_map.count() == 0;
             },
             else => unreachable,
         }
@@ -4883,6 +4781,10 @@ pub const Type = extern union {
             .bool,
             .type,
             .anyerror,
+            .error_union,
+            .error_set_single,
+            .error_set,
+            .error_set_merged,
             .fn_noreturn_no_args,
             .fn_void_no_args,
             .fn_naked_noreturn_no_args,
@@ -4939,42 +4841,6 @@ pub const Type = extern union {
                 }
             },
 
-            .error_union => {
-                const error_ty = ty.errorUnionSet();
-                switch (error_ty.errorSetCardinality()) {
-                    .zero => {
-                        const payload_ty = ty.errorUnionPayload();
-                        if (onePossibleValue(payload_ty)) |payload_val| {
-                            _ = payload_val;
-                            return Value.initTag(.the_only_possible_value);
-                        } else {
-                            return null;
-                        }
-                    },
-                    .one => {
-                        if (ty.errorUnionPayload().isNoReturn()) {
-                            const error_val = onePossibleValue(error_ty).?;
-                            return error_val;
-                        } else {
-                            return null;
-                        }
-                    },
-                    .many => return null,
-                }
-            },
-
-            .error_set_single => return Value.initTag(.the_only_possible_value),
-            .error_set => {
-                const err_set_obj = ty.castTag(.error_set).?.data;
-                if (err_set_obj.names.count() > 1) return null;
-                return Value.initTag(.the_only_possible_value);
-            },
-            .error_set_merged => {
-                const name_map = ty.castTag(.error_set_merged).?.data;
-                if (name_map.count() > 1) return null;
-                return Value.initTag(.the_only_possible_value);
-            },
-
             .@"struct" => {
                 const s = ty.castTag(.@"struct").?.data;
                 assert(s.haveFieldTypes());
test/behavior/error.zig
@@ -453,65 +453,6 @@ test "optional error set is the same size as error set" {
     comptime try expect(S.returnsOptErrSet() == null);
 }
 
-test "optional error set with only one error is the same size as bool" {
-    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
-
-    const E = error{only};
-    comptime try expect(@sizeOf(?E) == @sizeOf(bool));
-    comptime try expect(@alignOf(?E) == @alignOf(bool));
-    const S = struct {
-        fn gimmeNull() ?E {
-            return null;
-        }
-        fn gimmeErr() ?E {
-            return error.only;
-        }
-    };
-    try expect(S.gimmeNull() == null);
-    try expect(error.only == S.gimmeErr().?);
-    comptime try expect(S.gimmeNull() == null);
-    comptime try expect(error.only == S.gimmeErr().?);
-}
-
-test "optional empty error set" {
-    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
-
-    comptime try expect(@sizeOf(error{}!void) == @sizeOf(void));
-    comptime try expect(@alignOf(error{}!void) == @alignOf(void));
-
-    var x: ?error{} = undefined;
-    if (x != null) {
-        @compileError("test failed");
-    }
-}
-
-test "empty error set plus zero-bit payload" {
-    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
-
-    comptime try expect(@sizeOf(error{}!void) == @sizeOf(void));
-    comptime try expect(@alignOf(error{}!void) == @alignOf(void));
-
-    var x: error{}!void = undefined;
-    if (x) |payload| {
-        if (payload != {}) {
-            @compileError("test failed");
-        }
-    } else |_| {
-        @compileError("test failed");
-    }
-    const S = struct {
-        fn empty() error{}!void {}
-        fn inferred() !void {
-            return empty();
-        }
-    };
-    try S.inferred();
-}
-
 test "nested catch" {
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO