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`.
This commit is contained in:
@@ -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,
|
||||
|
||||
59
src/Sema.zig
59
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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
192
src/type.zig
192
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());
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user