From 0333ff4476d0132a2397122dcab964de7fc0f2d3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 10 Jun 2022 14:11:59 +0300 Subject: [PATCH] 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`. --- lib/std/compress/deflate/compressor_test.zig | 2 - src/Sema.zig | 59 ++---- src/arch/aarch64/CodeGen.zig | 11 +- src/arch/arm/CodeGen.zig | 12 +- src/arch/wasm/CodeGen.zig | 58 ++---- src/arch/x86_64/CodeGen.zig | 21 +- src/codegen.zig | 9 - src/codegen/c.zig | 78 +++----- src/codegen/llvm.zig | 98 +++------- src/type.zig | 192 +++---------------- test/behavior/error.zig | 59 ------ 11 files changed, 123 insertions(+), 476 deletions(-) diff --git a/lib/std/compress/deflate/compressor_test.zig b/lib/std/compress/deflate/compressor_test.zig index db07a07064..4f8efd0d6e 100644 --- a/lib/std/compress/deflate/compressor_test.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index 7dbf3d921f..9d09324193 100644 --- a/src/Sema.zig +++ b/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; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 7a7ee31117..e74fbd44ac 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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()) { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 2bf57943c3..3cc853154b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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()) { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 33cf5422f2..d799dda5bb 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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); + 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), - }); + // 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) { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5bda84c98c..c94eaa37af 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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()) { diff --git a/src/codegen.zig b/src/codegen.zig index 84615d86d8..025decdb4b 100644 --- a/src/codegen.zig +++ b/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()) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 45191f3107..949e2a67f2 100644 --- a/src/codegen/c.zig +++ b/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 { - try writer.writeAll("if("); + 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) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8031c4635e..efe6a754b6 100644 --- a/src/codegen/llvm.zig +++ b/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; + + 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 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 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; diff --git a/src/type.zig b/src/type.zig index 8556526e18..0ca7ba83c5 100644 --- a/src/type.zig +++ b/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()); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index ac51ec1eae..c9697a0f82 100644 --- a/test/behavior/error.zig +++ b/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