Skip to content

Commit 8373520

Browse files
authored
Merge pull request #11699 from ziglang/empty-error-sets
stage2: fixes for error union semantics
2 parents a0775fd + 60af427 commit 8373520

File tree

14 files changed

+1553
-727
lines changed

14 files changed

+1553
-727
lines changed

lib/std/debug.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,7 @@ fn resetSegfaultHandler() void {
17981798
.mask = os.empty_sigset,
17991799
.flags = 0,
18001800
};
1801-
// do nothing if an error happens to avoid a double-panic
1801+
// To avoid a double-panic, do nothing if an error happens here.
18021802
updateSegfaultHandler(&act) catch {};
18031803
}
18041804

src/Sema.zig

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5899,12 +5899,22 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
58995899
if (val.isUndef()) {
59005900
return sema.addConstUndef(result_ty);
59015901
}
5902-
const payload = try sema.arena.create(Value.Payload.U64);
5903-
payload.* = .{
5904-
.base = .{ .tag = .int_u64 },
5905-
.data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
5906-
};
5907-
return sema.addConstant(result_ty, Value.initPayload(&payload.base));
5902+
switch (val.tag()) {
5903+
.@"error" => {
5904+
const payload = try sema.arena.create(Value.Payload.U64);
5905+
payload.* = .{
5906+
.base = .{ .tag = .int_u64 },
5907+
.data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
5908+
};
5909+
return sema.addConstant(result_ty, Value.initPayload(&payload.base));
5910+
},
5911+
5912+
// This is not a valid combination with the type `anyerror`.
5913+
.the_only_possible_value => unreachable,
5914+
5915+
// Assume it's already encoded as an integer.
5916+
else => return sema.addConstant(result_ty, val),
5917+
}
59085918
}
59095919

59105920
try sema.requireRuntimeBlock(block, src);
@@ -6261,19 +6271,24 @@ fn zirErrUnionPayload(
62616271
});
62626272
}
62636273

6274+
const result_ty = operand_ty.errorUnionPayload();
62646275
if (try sema.resolveDefinedValue(block, src, operand)) |val| {
62656276
if (val.getError()) |name| {
62666277
return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
62676278
}
62686279
const data = val.castTag(.eu_payload).?.data;
6269-
const result_ty = operand_ty.errorUnionPayload();
62706280
return sema.addConstant(result_ty, data);
62716281
}
6282+
62726283
try sema.requireRuntimeBlock(block, src);
6273-
if (safety_check and block.wantSafety()) {
6284+
6285+
// If the error set has no fields then no safety check is needed.
6286+
if (safety_check and block.wantSafety() and
6287+
operand_ty.errorUnionSet().errorSetCardinality() != .zero)
6288+
{
62746289
try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
62756290
}
6276-
const result_ty = operand_ty.errorUnionPayload();
6291+
62776292
return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
62786293
}
62796294

@@ -6311,7 +6326,8 @@ fn analyzeErrUnionPayloadPtr(
63116326
});
63126327
}
63136328

6314-
const payload_ty = operand_ty.elemType().errorUnionPayload();
6329+
const err_union_ty = operand_ty.elemType();
6330+
const payload_ty = err_union_ty.errorUnionPayload();
63156331
const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{
63166332
.pointee_type = payload_ty,
63176333
.mutable = !operand_ty.isConstPtr(),
@@ -6351,9 +6367,14 @@ fn analyzeErrUnionPayloadPtr(
63516367
}
63526368

63536369
try sema.requireRuntimeBlock(block, src);
6354-
if (safety_check and block.wantSafety()) {
6370+
6371+
// If the error set has no fields then no safety check is needed.
6372+
if (safety_check and block.wantSafety() and
6373+
err_union_ty.errorUnionSet().errorSetCardinality() != .zero)
6374+
{
63556375
try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
63566376
}
6377+
63576378
const air_tag: Air.Inst.Tag = if (initializing)
63586379
.errunion_payload_ptr_set
63596380
else
@@ -20929,6 +20950,11 @@ fn analyzeLoad(
2092920950
.Pointer => ptr_ty.childType(),
2093020951
else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}),
2093120952
};
20953+
20954+
if (try sema.typeHasOnePossibleValue(block, src, elem_ty)) |opv| {
20955+
return sema.addConstant(elem_ty, opv);
20956+
}
20957+
2093220958
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
2093320959
if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| {
2093420960
return sema.addConstant(elem_ty, elem_val);
@@ -23295,16 +23321,11 @@ pub fn typeHasOnePossibleValue(
2329523321
.const_slice,
2329623322
.mut_slice,
2329723323
.anyopaque,
23298-
.optional,
2329923324
.optional_single_mut_pointer,
2330023325
.optional_single_const_pointer,
2330123326
.enum_literal,
2330223327
.anyerror_void_error_union,
23303-
.error_union,
23304-
.error_set,
23305-
.error_set_single,
2330623328
.error_set_inferred,
23307-
.error_set_merged,
2330823329
.@"opaque",
2330923330
.var_args_param,
2331023331
.manyptr_u8,
@@ -23333,6 +23354,56 @@ pub fn typeHasOnePossibleValue(
2333323354
.bound_fn,
2333423355
=> return null,
2333523356

23357+
.optional => {
23358+
var buf: Type.Payload.ElemType = undefined;
23359+
const child_ty = ty.optionalChild(&buf);
23360+
if (child_ty.isNoReturn()) {
23361+
return Value.@"null";
23362+
} else {
23363+
return null;
23364+
}
23365+
},
23366+
23367+
.error_union => {
23368+
const error_ty = ty.errorUnionSet();
23369+
switch (error_ty.errorSetCardinality()) {
23370+
.zero => {
23371+
const payload_ty = ty.errorUnionPayload();
23372+
if (try typeHasOnePossibleValue(sema, block, src, payload_ty)) |payload_val| {
23373+
return try Value.Tag.eu_payload.create(sema.arena, payload_val);
23374+
} else {
23375+
return null;
23376+
}
23377+
},
23378+
.one => {
23379+
if (ty.errorUnionPayload().isNoReturn()) {
23380+
const error_val = (try typeHasOnePossibleValue(sema, block, src, error_ty)).?;
23381+
return error_val;
23382+
} else {
23383+
return null;
23384+
}
23385+
},
23386+
.many => return null,
23387+
}
23388+
},
23389+
23390+
.error_set_single => {
23391+
const name = ty.castTag(.error_set_single).?.data;
23392+
return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
23393+
},
23394+
.error_set => {
23395+
const err_set_obj = ty.castTag(.error_set).?.data;
23396+
const names = err_set_obj.names.keys();
23397+
if (names.len > 1) return null;
23398+
return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] });
23399+
},
23400+
.error_set_merged => {
23401+
const name_map = ty.castTag(.error_set_merged).?.data;
23402+
const names = name_map.keys();
23403+
if (names.len > 1) return null;
23404+
return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] });
23405+
},
23406+
2333623407
.@"struct" => {
2333723408
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
2333823409
const s = resolved_ty.castTag(.@"struct").?.data;

src/arch/aarch64/CodeGen.zig

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33
const mem = std.mem;
44
const math = std.math;
55
const assert = std.debug.assert;
6+
const codegen = @import("../../codegen.zig");
67
const Air = @import("../../Air.zig");
78
const Mir = @import("Mir.zig");
89
const Emit = @import("Emit.zig");
@@ -22,12 +23,14 @@ const leb128 = std.leb;
2223
const log = std.log.scoped(.codegen);
2324
const build_options = @import("build_options");
2425

25-
const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
26-
const FnResult = @import("../../codegen.zig").FnResult;
27-
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
26+
const GenerateSymbolError = codegen.GenerateSymbolError;
27+
const FnResult = codegen.FnResult;
28+
const DebugInfoOutput = codegen.DebugInfoOutput;
2829

2930
const bits = @import("bits.zig");
3031
const abi = @import("abi.zig");
32+
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33+
const errUnionErrorOffset = codegen.errUnionErrorOffset;
3134
const RegisterManager = abi.RegisterManager;
3235
const RegisterLock = RegisterManager.RegisterLock;
3336
const Register = bits.Register;
@@ -3272,7 +3275,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
32723275

32733276
fn ret(self: *Self, mcv: MCValue) !void {
32743277
const ret_ty = self.fn_type.fnReturnType();
3275-
try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
3278+
switch (self.ret_mcv) {
3279+
.immediate => {
3280+
assert(ret_ty.isError());
3281+
},
3282+
else => {
3283+
try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
3284+
},
3285+
}
32763286
// Just add space for an instruction, patch this later
32773287
const index = try self.addInst(.{
32783288
.tag = .nop,
@@ -3601,30 +3611,39 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
36013611
const error_type = ty.errorUnionSet();
36023612
const payload_type = ty.errorUnionPayload();
36033613

3604-
if (!error_type.hasRuntimeBits()) {
3614+
if (error_type.errorSetCardinality() == .zero) {
36053615
return MCValue{ .immediate = 0 }; // always false
3606-
} else if (!payload_type.hasRuntimeBits()) {
3607-
if (error_type.abiSize(self.target.*) <= 8) {
3608-
const reg_mcv: MCValue = switch (operand) {
3609-
.register => operand,
3610-
else => .{ .register = try self.copyToTmpRegister(error_type, operand) },
3611-
};
3616+
}
36123617

3618+
const err_off = errUnionErrorOffset(payload_type, self.target.*);
3619+
switch (operand) {
3620+
.stack_offset => |off| {
3621+
const offset = off - @intCast(u32, err_off);
3622+
const tmp_reg = try self.copyToTmpRegister(Type.anyerror, .{ .stack_offset = offset });
36133623
_ = try self.addInst(.{
36143624
.tag = .cmp_immediate,
36153625
.data = .{ .r_imm12_sh = .{
3616-
.rn = reg_mcv.register,
3626+
.rn = tmp_reg,
36173627
.imm12 = 0,
36183628
} },
36193629
});
3620-
3621-
return MCValue{ .compare_flags_unsigned = .gt };
3622-
} else {
3623-
return self.fail("TODO isErr for errors with size > 8", .{});
3624-
}
3625-
} else {
3626-
return self.fail("TODO isErr for non-empty payloads", .{});
3630+
},
3631+
.register => |reg| {
3632+
if (err_off > 0 or payload_type.hasRuntimeBitsIgnoreComptime()) {
3633+
return self.fail("TODO implement isErr for register operand with payload bits", .{});
3634+
}
3635+
_ = try self.addInst(.{
3636+
.tag = .cmp_immediate,
3637+
.data = .{ .r_imm12_sh = .{
3638+
.rn = reg,
3639+
.imm12 = 0,
3640+
} },
3641+
});
3642+
},
3643+
else => return self.fail("TODO implement isErr for {}", .{operand}),
36273644
}
3645+
3646+
return MCValue{ .compare_flags_unsigned = .gt };
36283647
}
36293648

36303649
fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
@@ -4483,15 +4502,15 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
44834502
const ref_int = @enumToInt(inst);
44844503
if (ref_int < Air.Inst.Ref.typed_value_map.len) {
44854504
const tv = Air.Inst.Ref.typed_value_map[ref_int];
4486-
if (!tv.ty.hasRuntimeBits()) {
4505+
if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) {
44874506
return MCValue{ .none = {} };
44884507
}
44894508
return self.genTypedValue(tv);
44904509
}
44914510

44924511
// If the type has no codegen bits, no need to store it.
44934512
const inst_ty = self.air.typeOf(inst);
4494-
if (!inst_ty.hasRuntimeBits())
4513+
if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError())
44954514
return MCValue{ .none = {} };
44964515

44974516
const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
@@ -4674,32 +4693,38 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
46744693
}
46754694
},
46764695
.ErrorSet => {
4677-
const err_name = typed_value.val.castTag(.@"error").?.data.name;
4678-
const module = self.bin_file.options.module.?;
4679-
const global_error_set = module.global_error_set;
4680-
const error_index = global_error_set.get(err_name).?;
4681-
return MCValue{ .immediate = error_index };
4696+
switch (typed_value.val.tag()) {
4697+
.@"error" => {
4698+
const err_name = typed_value.val.castTag(.@"error").?.data.name;
4699+
const module = self.bin_file.options.module.?;
4700+
const global_error_set = module.global_error_set;
4701+
const error_index = global_error_set.get(err_name).?;
4702+
return MCValue{ .immediate = error_index };
4703+
},
4704+
else => {
4705+
// In this case we are rendering an error union which has a 0 bits payload.
4706+
return MCValue{ .immediate = 0 };
4707+
},
4708+
}
46824709
},
46834710
.ErrorUnion => {
46844711
const error_type = typed_value.ty.errorUnionSet();
46854712
const payload_type = typed_value.ty.errorUnionPayload();
46864713

4687-
if (typed_value.val.castTag(.eu_payload)) |pl| {
4688-
if (!payload_type.hasRuntimeBits()) {
4689-
// We use the error type directly as the type.
4690-
return MCValue{ .immediate = 0 };
4691-
}
4714+
if (error_type.errorSetCardinality() == .zero) {
4715+
const payload_val = typed_value.val.castTag(.eu_payload).?.data;
4716+
return self.genTypedValue(.{ .ty = payload_type, .val = payload_val });
4717+
}
46924718

4693-
_ = pl;
4694-
return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()});
4695-
} else {
4696-
if (!payload_type.hasRuntimeBits()) {
4697-
// We use the error type directly as the type.
4698-
return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
4699-
}
4719+
const is_pl = typed_value.val.errorUnionIsPayload();
47004720

4701-
return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()});
4721+
if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
4722+
// We use the error type directly as the type.
4723+
const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero);
4724+
return self.genTypedValue(.{ .ty = error_type, .val = err_val });
47024725
}
4726+
4727+
return self.lowerUnnamedConst(typed_value);
47034728
},
47044729
.Struct => {
47054730
return self.lowerUnnamedConst(typed_value);
@@ -4796,13 +4821,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
47964821

47974822
if (ret_ty.zigTypeTag() == .NoReturn) {
47984823
result.return_value = .{ .unreach = {} };
4799-
} else if (!ret_ty.hasRuntimeBits()) {
4824+
} else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
48004825
result.return_value = .{ .none = {} };
48014826
} else switch (cc) {
48024827
.Naked => unreachable,
48034828
.Unspecified, .C => {
48044829
const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
4805-
if (ret_ty_size <= 8) {
4830+
if (ret_ty_size == 0) {
4831+
assert(ret_ty.isError());
4832+
result.return_value = .{ .immediate = 0 };
4833+
} else if (ret_ty_size <= 8) {
48064834
result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) };
48074835
} else {
48084836
return self.fail("TODO support more return types for ARM backend", .{});

0 commit comments

Comments
 (0)