authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-22 21:51:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 15:34:52-07:00
log3264abe3d8f658e1b7275d2be80e43eddfc098dc
treec4cb6d837e163fb19da5591e4cea90684b52e316
parent60f0acd9b9c2bced47ba1c214460f34b73738f95

stage2: fixes for error union semantics

* Sema: avoid unnecessary safety checks when an error set is empty. * Sema: make zirErrorToInt handle comptime errors that are represented as integers. * Sema: make empty error sets properly integrate with typeHasOnePossibleValue. * Type: correct the ABI alignment and size of error unions which have both zero-bit error set and zero-bit payload. The previous code did not account for the fact that we still need to store a bit for whether there is an error. * LLVM: lower error unions possibly with the payload first or with the error code first, depending on alignment. Previously it always put the error code first and used a padding array. * LLVM: lower functions which have an empty error set as the return type the same as anyerror, so that they can be used where fn()anyerror function pointers are expected. In such functions, Zig will lower ret to returning zero instead of void. As a result, one more behavior test is passing.

5 files changed, 369 insertions(+), 124 deletions(-)

lib/std/debug.zig+1-1
......@@ -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+49-14
......@@ -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
......@@ -23301,10 +23322,7 @@ pub fn typeHasOnePossibleValue(
2330123322 .enum_literal,
2330223323 .anyerror_void_error_union,
2330323324 .error_union,
23304 .error_set,
23305 .error_set_single,
2330623325 .error_set_inferred,
23307 .error_set_merged,
2330823326 .@"opaque",
2330923327 .var_args_param,
2331023328 .manyptr_u8,
......@@ -23333,6 +23351,23 @@ pub fn typeHasOnePossibleValue(
2333323351 .bound_fn,
2333423352 => return null,
2333523353
23354 .error_set_single => {
23355 const name = ty.castTag(.error_set_single).?.data;
23356 return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
23357 },
23358 .error_set => {
23359 const err_set_obj = ty.castTag(.error_set).?.data;
23360 const names = err_set_obj.names.keys();
23361 if (names.len > 1) return null;
23362 return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] });
23363 },
23364 .error_set_merged => {
23365 const name_map = ty.castTag(.error_set_merged).?.data;
23366 const names = name_map.keys();
23367 if (names.len > 1) return null;
23368 return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] });
23369 },
23370
2333623371 .@"struct" => {
2333723372 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
2333823373 const s = resolved_ty.castTag(.@"struct").?.data;
src/codegen/llvm.zig+139-61
......@@ -2451,20 +2451,22 @@ pub const DeclGen = struct {
24512451 .ErrorUnion => {
24522452 const error_type = t.errorUnionSet();
24532453 const payload_type = t.errorUnionPayload();
2454 const llvm_error_type = try dg.llvmType(error_type);
2454 if (error_type.errorSetCardinality() == .zero) {
2455 return dg.llvmType(payload_type);
2456 }
24552457 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
2456 return llvm_error_type;
2458 return try dg.llvmType(Type.anyerror);
24572459 }
2460 const llvm_error_type = try dg.llvmType(error_type);
24582461 const llvm_payload_type = try dg.llvmType(payload_type);
24592462
24602463 const payload_align = payload_type.abiAlignment(target);
2461 const error_size = error_type.abiSize(target);
2462 if (payload_align > error_size) {
2463 const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size));
2464 const fields: [3]*const llvm.Type = .{ llvm_error_type, pad_type, llvm_payload_type };
2464 const error_align = Type.anyerror.abiAlignment(target);
2465 if (error_align > payload_align) {
2466 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
24652467 return dg.context.structType(&fields, fields.len, .False);
24662468 } else {
2467 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
2469 const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type };
24682470 return dg.context.structType(&fields, fields.len, .False);
24692471 }
24702472 },
......@@ -3103,6 +3105,10 @@ pub const DeclGen = struct {
31033105 .ErrorUnion => {
31043106 const error_type = tv.ty.errorUnionSet();
31053107 const payload_type = tv.ty.errorUnionPayload();
3108 if (error_type.errorSetCardinality() == .zero) {
3109 const payload_val = tv.val.castTag(.eu_payload).?.data;
3110 return dg.genTypedValue(.{ .ty = payload_type, .val = payload_val });
3111 }
31063112 const is_pl = tv.val.errorUnionIsPayload();
31073113
31083114 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
......@@ -3110,28 +3116,24 @@ pub const DeclGen = struct {
31103116 const err_val = if (!is_pl) tv.val else Value.initTag(.zero);
31113117 return dg.genTypedValue(.{ .ty = error_type, .val = err_val });
31123118 }
3113 var len: u8 = 2;
3114 var fields: [3]*const llvm.Value = .{
3115 try dg.genTypedValue(.{
3116 .ty = error_type,
3117 .val = if (is_pl) Value.initTag(.zero) else tv.val,
3118 }),
3119 try dg.genTypedValue(.{
3120 .ty = payload_type,
3121 .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
3122 }),
3123 undefined,
3124 };
31253119
31263120 const payload_align = payload_type.abiAlignment(target);
3127 const error_size = error_type.abiSize(target);
3128 if (payload_align > error_size) {
3129 fields[2] = fields[1];
3130 const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size));
3131 fields[1] = pad_type.getUndef();
3132 len += 1;
3121 const error_align = Type.anyerror.abiAlignment(target);
3122 const llvm_error_value = try dg.genTypedValue(.{
3123 .ty = error_type,
3124 .val = if (is_pl) Value.initTag(.zero) else tv.val,
3125 });
3126 const llvm_payload_value = try dg.genTypedValue(.{
3127 .ty = payload_type,
3128 .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
3129 });
3130 if (error_align > payload_align) {
3131 const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value };
3132 return dg.context.constStruct(&fields, fields.len, .False);
3133 } else {
3134 const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value };
3135 return dg.context.constStruct(&fields, fields.len, .False);
31333136 }
3134 return dg.context.constStruct(&fields, len, .False);
31353137 },
31363138 .Struct => {
31373139 const llvm_struct_ty = try dg.llvmType(tv.ty);
......@@ -4338,11 +4340,19 @@ pub const FuncGen = struct {
43384340 _ = self.builder.buildRetVoid();
43394341 return null;
43404342 }
4343 const fn_info = self.dg.decl.ty.fnInfo();
43414344 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
4342 _ = self.builder.buildRetVoid();
4345 if (fn_info.return_type.isError()) {
4346 // Functions with an empty error set are emitted with an error code
4347 // return type and return zero so they can be function pointers coerced
4348 // to functions that return anyerror.
4349 const err_int = try self.dg.llvmType(Type.anyerror);
4350 _ = self.builder.buildRet(err_int.constInt(0, .False));
4351 } else {
4352 _ = self.builder.buildRetVoid();
4353 }
43434354 return null;
43444355 }
4345 const fn_info = self.dg.decl.ty.fnInfo();
43464356 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
43474357 const operand = try self.resolveInst(un_op);
43484358 const llvm_ret_ty = operand.typeOf();
......@@ -4369,13 +4379,25 @@ pub const FuncGen = struct {
43694379 const un_op = self.air.instructions.items(.data)[inst].un_op;
43704380 const ptr_ty = self.air.typeOf(un_op);
43714381 const ret_ty = ptr_ty.childType();
4372 if (!ret_ty.hasRuntimeBitsIgnoreComptime() or self.ret_ptr != null) {
4382 const fn_info = self.dg.decl.ty.fnInfo();
4383 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
4384 if (fn_info.return_type.isError()) {
4385 // Functions with an empty error set are emitted with an error code
4386 // return type and return zero so they can be function pointers coerced
4387 // to functions that return anyerror.
4388 const err_int = try self.dg.llvmType(Type.anyerror);
4389 _ = self.builder.buildRet(err_int.constInt(0, .False));
4390 } else {
4391 _ = self.builder.buildRetVoid();
4392 }
4393 return null;
4394 }
4395 if (self.ret_ptr != null) {
43734396 _ = self.builder.buildRetVoid();
43744397 return null;
43754398 }
43764399 const ptr = try self.resolveInst(un_op);
43774400 const target = self.dg.module.getTarget();
4378 const fn_info = self.dg.decl.ty.fnInfo();
43794401 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
43804402 const llvm_ret_ty = try self.dg.llvmType(ret_ty);
43814403 const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: {
......@@ -5433,18 +5455,30 @@ pub const FuncGen = struct {
54335455 const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror));
54345456 const zero = err_set_ty.constNull();
54355457
5458 if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
5459 const llvm_i1 = self.context.intType(1);
5460 switch (op) {
5461 .EQ => return llvm_i1.constInt(1, .False), // 0 == 0
5462 .NE => return llvm_i1.constInt(0, .False), // 0 != 0
5463 else => unreachable,
5464 }
5465 }
5466
54365467 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
54375468 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
54385469 return self.builder.buildICmp(op, loaded, zero, "");
54395470 }
54405471
5472 const target = self.dg.module.getTarget();
5473 const err_field_index = errUnionErrorOffset(payload_ty, target);
5474
54415475 if (operand_is_ptr or isByRef(err_union_ty)) {
5442 const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");
5476 const err_field_ptr = self.builder.buildStructGEP(operand, err_field_index, "");
54435477 const loaded = self.builder.buildLoad(err_field_ptr, "");
54445478 return self.builder.buildICmp(op, loaded, zero, "");
54455479 }
54465480
5447 const loaded = self.builder.buildExtractValue(operand, 0, "");
5481 const loaded = self.builder.buildExtractValue(operand, err_field_index, "");
54485482 return self.builder.buildICmp(op, loaded, zero, "");
54495483 }
54505484
......@@ -5544,11 +5578,17 @@ pub const FuncGen = struct {
55445578
55455579 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
55465580 const operand = try self.resolveInst(ty_op.operand);
5547 const result_ty = self.air.getRefType(ty_op.ty);
5581 const operand_ty = self.air.typeOf(ty_op.operand);
5582 const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
5583 // If the error set has no fields, then the payload and the error
5584 // union are the same value.
5585 if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
5586 return operand;
5587 }
5588 const result_ty = self.air.typeOfIndex(inst);
55485589 const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
5549
55505590 const target = self.dg.module.getTarget();
5551 const offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;
5591 const offset = errUnionPayloadOffset(payload_ty, target);
55525592
55535593 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
55545594 if (!operand_is_ptr) return null;
......@@ -5574,54 +5614,70 @@ pub const FuncGen = struct {
55745614 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
55755615 const operand = try self.resolveInst(ty_op.operand);
55765616 const operand_ty = self.air.typeOf(ty_op.operand);
5577 const err_set_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
5617 const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
5618 if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
5619 const err_llvm_ty = try self.dg.llvmType(Type.anyerror);
5620 if (operand_is_ptr) {
5621 return self.builder.buildBitCast(operand, err_llvm_ty.pointerType(0), "");
5622 } else {
5623 return err_llvm_ty.constInt(0, .False);
5624 }
5625 }
55785626
5579 const payload_ty = err_set_ty.errorUnionPayload();
5627 const payload_ty = err_union_ty.errorUnionPayload();
55805628 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
55815629 if (!operand_is_ptr) return operand;
55825630 return self.builder.buildLoad(operand, "");
55835631 }
55845632
5585 if (operand_is_ptr or isByRef(err_set_ty)) {
5586 const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");
5633 const target = self.dg.module.getTarget();
5634 const offset = errUnionErrorOffset(payload_ty, target);
5635
5636 if (operand_is_ptr or isByRef(err_union_ty)) {
5637 const err_field_ptr = self.builder.buildStructGEP(operand, offset, "");
55875638 return self.builder.buildLoad(err_field_ptr, "");
55885639 }
55895640
5590 return self.builder.buildExtractValue(operand, 0, "");
5641 return self.builder.buildExtractValue(operand, offset, "");
55915642 }
55925643
55935644 fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
55945645 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
55955646 const operand = try self.resolveInst(ty_op.operand);
5596 const error_set_ty = self.air.typeOf(ty_op.operand).childType();
5647 const error_union_ty = self.air.typeOf(ty_op.operand).childType();
55975648
5598 const error_ty = error_set_ty.errorUnionSet();
5599 const payload_ty = error_set_ty.errorUnionPayload();
5649 const error_ty = error_union_ty.errorUnionSet();
5650 if (error_ty.errorSetCardinality() == .zero) {
5651 // TODO: write undefined bytes through the pointer here
5652 return operand;
5653 }
5654 const payload_ty = error_union_ty.errorUnionPayload();
56005655 const non_error_val = try self.dg.genTypedValue(.{ .ty = error_ty, .val = Value.zero });
56015656 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5602 // We have a pointer to a i1. We need to set it to 1 and then return the same pointer.
56035657 _ = self.builder.buildStore(non_error_val, operand);
56045658 return operand;
56055659 }
56065660 const index_type = self.context.intType(32);
5661 const target = self.dg.module.getTarget();
56075662 {
5663 const error_offset = errUnionErrorOffset(payload_ty, target);
56085664 // First set the non-error value.
56095665 const indices: [2]*const llvm.Value = .{
56105666 index_type.constNull(), // dereference the pointer
5611 index_type.constNull(), // first field is the payload
5667 index_type.constInt(error_offset, .False),
56125668 };
56135669 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
5614 _ = self.builder.buildStore(non_error_val, non_null_ptr);
5670 const store_inst = self.builder.buildStore(non_error_val, non_null_ptr);
5671 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
56155672 }
56165673 // Then return the payload pointer (only if it is used).
56175674 if (self.liveness.isUnused(inst))
56185675 return null;
56195676
5620 const target = self.dg.module.getTarget();
5621 const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;
5677 const payload_offset = errUnionPayloadOffset(payload_ty, target);
56225678 const indices: [2]*const llvm.Value = .{
56235679 index_type.constNull(), // dereference the pointer
5624 index_type.constInt(payload_offset, .False), // second field is the payload
5680 index_type.constInt(payload_offset, .False),
56255681 };
56265682 return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
56275683 }
......@@ -5669,21 +5725,26 @@ pub const FuncGen = struct {
56695725 if (self.liveness.isUnused(inst)) return null;
56705726
56715727 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5672 const payload_ty = self.air.typeOf(ty_op.operand);
5728 const inst_ty = self.air.typeOfIndex(inst);
56735729 const operand = try self.resolveInst(ty_op.operand);
5730 if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) {
5731 return operand;
5732 }
5733 const payload_ty = self.air.typeOf(ty_op.operand);
56745734 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
56755735 return operand;
56765736 }
5677 const inst_ty = self.air.typeOfIndex(inst);
5678 const ok_err_code = self.context.intType(16).constNull();
5737 const ok_err_code = (try self.dg.llvmType(Type.anyerror)).constNull();
56795738 const err_un_llvm_ty = try self.dg.llvmType(inst_ty);
56805739
56815740 const target = self.dg.module.getTarget();
5682 const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;
5741 const payload_offset = errUnionPayloadOffset(payload_ty, target);
5742 const error_offset = errUnionErrorOffset(payload_ty, target);
56835743 if (isByRef(inst_ty)) {
56845744 const result_ptr = self.buildAlloca(err_un_llvm_ty);
5685 const err_ptr = self.builder.buildStructGEP(result_ptr, 0, "");
5686 _ = self.builder.buildStore(ok_err_code, err_ptr);
5745 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");
5746 const store_inst = self.builder.buildStore(ok_err_code, err_ptr);
5747 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
56875748 const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, "");
56885749 var ptr_ty_payload: Type.Payload.ElemType = .{
56895750 .base = .{ .tag = .single_mut_pointer },
......@@ -5694,7 +5755,7 @@ pub const FuncGen = struct {
56945755 return result_ptr;
56955756 }
56965757
5697 const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), ok_err_code, 0, "");
5758 const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), ok_err_code, error_offset, "");
56985759 return self.builder.buildInsertValue(partial, operand, payload_offset, "");
56995760 }
57005761
......@@ -5711,11 +5772,13 @@ pub const FuncGen = struct {
57115772 const err_un_llvm_ty = try self.dg.llvmType(err_un_ty);
57125773
57135774 const target = self.dg.module.getTarget();
5714 const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;
5775 const payload_offset = errUnionPayloadOffset(payload_ty, target);
5776 const error_offset = errUnionErrorOffset(payload_ty, target);
57155777 if (isByRef(err_un_ty)) {
57165778 const result_ptr = self.buildAlloca(err_un_llvm_ty);
5717 const err_ptr = self.builder.buildStructGEP(result_ptr, 0, "");
5718 _ = self.builder.buildStore(operand, err_ptr);
5779 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");
5780 const store_inst = self.builder.buildStore(operand, err_ptr);
5781 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
57195782 const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, "");
57205783 var ptr_ty_payload: Type.Payload.ElemType = .{
57215784 .base = .{ .tag = .single_mut_pointer },
......@@ -5728,7 +5791,7 @@ pub const FuncGen = struct {
57285791 return result_ptr;
57295792 }
57305793
5731 const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), operand, 0, "");
5794 const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), operand, error_offset, "");
57325795 // TODO set payload bytes to undef
57335796 return partial;
57345797 }
......@@ -8546,7 +8609,14 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
85468609/// be effectively bitcasted to the actual return type.
85478610fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.Type {
85488611 if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {
8549 return dg.context.voidType();
8612 // If the return type is an error set or an error union, then we make this
8613 // anyerror return type instead, so that it can be coerced into a function
8614 // pointer type which has anyerror as the return type.
8615 if (fn_info.return_type.isError()) {
8616 return dg.llvmType(Type.anyerror);
8617 } else {
8618 return dg.context.voidType();
8619 }
85508620 }
85518621 const target = dg.module.getTarget();
85528622 switch (fn_info.cc) {
......@@ -8991,3 +9061,11 @@ fn buildAllocaInner(
89919061
89929062 return builder.buildAlloca(llvm_ty, "");
89939063}
9064
9065fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u1 {
9066 return @boolToInt(Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target));
9067}
9068
9069fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u1 {
9070 return @boolToInt(Type.anyerror.abiAlignment(target) <= payload_ty.abiAlignment(target));
9071}
src/type.zig+152-45
......@@ -2317,10 +2317,7 @@ pub const Type = extern union {
23172317 .const_slice_u8_sentinel_0,
23182318 .array_u8_sentinel_0,
23192319 .anyerror_void_error_union,
2320 .error_set,
2321 .error_set_single,
23222320 .error_set_inferred,
2323 .error_set_merged,
23242321 .manyptr_u8,
23252322 .manyptr_const_u8,
23262323 .manyptr_const_u8_sentinel_0,
......@@ -2361,8 +2358,20 @@ pub const Type = extern union {
23612358 .fn_void_no_args,
23622359 .fn_naked_noreturn_no_args,
23632360 .fn_ccc_void_no_args,
2361 .error_set_single,
23642362 => return false,
23652363
2364 .error_set => {
2365 const err_set_obj = ty.castTag(.error_set).?.data;
2366 const names = err_set_obj.names.keys();
2367 return names.len > 1;
2368 },
2369 .error_set_merged => {
2370 const name_map = ty.castTag(.error_set_merged).?.data;
2371 const names = name_map.keys();
2372 return names.len > 1;
2373 },
2374
23662375 // These types have more than one possible value, so the result is the same as
23672376 // asking whether they are comptime-only types.
23682377 .anyframe_T,
......@@ -2388,6 +2397,21 @@ pub const Type = extern union {
23882397 }
23892398 },
23902399
2400 .error_union => {
2401 // This code needs to be kept in sync with the equivalent switch prong
2402 // in abiSizeAdvanced.
2403 const data = ty.castTag(.error_union).?.data;
2404 if (data.error_set.errorSetCardinality() == .zero) {
2405 return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit);
2406 } else if (ignore_comptime_only) {
2407 return true;
2408 } else if (sema_kit) |sk| {
2409 return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
2410 } else {
2411 return !comptimeOnly(ty);
2412 }
2413 },
2414
23912415 .@"struct" => {
23922416 const struct_obj = ty.castTag(.@"struct").?.data;
23932417 if (sema_kit) |sk| {
......@@ -2467,12 +2491,6 @@ pub const Type = extern union {
24672491
24682492 .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0,
24692493
2470 .error_union => {
2471 const payload = ty.castTag(.error_union).?.data;
2472 return (try payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) or
2473 (try payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit));
2474 },
2475
24762494 .tuple, .anon_struct => {
24772495 const tuple = ty.tupleFields();
24782496 for (tuple.types) |field_ty, i| {
......@@ -2852,13 +2870,30 @@ pub const Type = extern union {
28522870 else => unreachable,
28532871 },
28542872
2855 .error_set,
2856 .error_set_single,
2873 // TODO revisit this when we have the concept of the error tag type
28572874 .anyerror_void_error_union,
28582875 .anyerror,
28592876 .error_set_inferred,
2860 .error_set_merged,
2861 => return AbiAlignmentAdvanced{ .scalar = 2 }, // TODO revisit this when we have the concept of the error tag type
2877 => return AbiAlignmentAdvanced{ .scalar = 2 },
2878
2879 .error_set => {
2880 const err_set_obj = ty.castTag(.error_set).?.data;
2881 const names = err_set_obj.names.keys();
2882 if (names.len <= 1) {
2883 return AbiAlignmentAdvanced{ .scalar = 0 };
2884 } else {
2885 return AbiAlignmentAdvanced{ .scalar = 2 };
2886 }
2887 },
2888 .error_set_merged => {
2889 const name_map = ty.castTag(.error_set_merged).?.data;
2890 const names = name_map.keys();
2891 if (names.len <= 1) {
2892 return AbiAlignmentAdvanced{ .scalar = 0 };
2893 } else {
2894 return AbiAlignmentAdvanced{ .scalar = 2 };
2895 }
2896 },
28622897
28632898 .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat),
28642899
......@@ -2900,31 +2935,29 @@ pub const Type = extern union {
29002935 },
29012936
29022937 .error_union => {
2938 // This code needs to be kept in sync with the equivalent switch prong
2939 // in abiSizeAdvanced.
29032940 const data = ty.castTag(.error_union).?.data;
2941 if (data.error_set.errorSetCardinality() == .zero) {
2942 return abiAlignmentAdvanced(data.payload, target, strat);
2943 }
2944 const code_align = abiAlignment(Type.anyerror, target);
29042945 switch (strat) {
29052946 .eager, .sema_kit => {
2906 if (!(try data.error_set.hasRuntimeBitsAdvanced(false, sema_kit))) {
2907 return data.payload.abiAlignmentAdvanced(target, strat);
2908 } else if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) {
2909 return data.error_set.abiAlignmentAdvanced(target, strat);
2947 if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) {
2948 return AbiAlignmentAdvanced{ .scalar = code_align };
29102949 }
29112950 return AbiAlignmentAdvanced{ .scalar = @maximum(
2951 code_align,
29122952 (try data.payload.abiAlignmentAdvanced(target, strat)).scalar,
2913 (try data.error_set.abiAlignmentAdvanced(target, strat)).scalar,
29142953 ) };
29152954 },
29162955 .lazy => |arena| {
29172956 switch (try data.payload.abiAlignmentAdvanced(target, strat)) {
29182957 .scalar => |payload_align| {
2919 if (payload_align == 0) {
2920 return data.error_set.abiAlignmentAdvanced(target, strat);
2921 }
2922 switch (try data.error_set.abiAlignmentAdvanced(target, strat)) {
2923 .scalar => |err_set_align| {
2924 return AbiAlignmentAdvanced{ .scalar = @maximum(payload_align, err_set_align) };
2925 },
2926 .val => {},
2927 }
2958 return AbiAlignmentAdvanced{
2959 .scalar = @maximum(code_align, payload_align),
2960 };
29282961 },
29292962 .val => {},
29302963 }
......@@ -3018,6 +3051,7 @@ pub const Type = extern union {
30183051 .@"undefined",
30193052 .enum_literal,
30203053 .type_info,
3054 .error_set_single,
30213055 => return AbiAlignmentAdvanced{ .scalar = 0 },
30223056
30233057 .noreturn,
......@@ -3136,6 +3170,7 @@ pub const Type = extern union {
31363170 .empty_struct_literal,
31373171 .empty_struct,
31383172 .void,
3173 .error_set_single,
31393174 => return AbiSizeAdvanced{ .scalar = 0 },
31403175
31413176 .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) {
......@@ -3291,14 +3326,30 @@ pub const Type = extern union {
32913326 },
32923327
32933328 // TODO revisit this when we have the concept of the error tag type
3294 .error_set,
3295 .error_set_single,
32963329 .anyerror_void_error_union,
32973330 .anyerror,
32983331 .error_set_inferred,
3299 .error_set_merged,
33003332 => return AbiSizeAdvanced{ .scalar = 2 },
33013333
3334 .error_set => {
3335 const err_set_obj = ty.castTag(.error_set).?.data;
3336 const names = err_set_obj.names.keys();
3337 if (names.len <= 1) {
3338 return AbiSizeAdvanced{ .scalar = 0 };
3339 } else {
3340 return AbiSizeAdvanced{ .scalar = 2 };
3341 }
3342 },
3343 .error_set_merged => {
3344 const name_map = ty.castTag(.error_set_merged).?.data;
3345 const names = name_map.keys();
3346 if (names.len <= 1) {
3347 return AbiSizeAdvanced{ .scalar = 0 };
3348 } else {
3349 return AbiSizeAdvanced{ .scalar = 2 };
3350 }
3351 },
3352
33023353 .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) },
33033354 .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) },
33043355 .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) },
......@@ -3325,24 +3376,42 @@ pub const Type = extern union {
33253376 },
33263377
33273378 .error_union => {
3379 // This code needs to be kept in sync with the equivalent switch prong
3380 // in abiAlignmentAdvanced.
33283381 const data = ty.castTag(.error_union).?.data;
3329 if (!data.error_set.hasRuntimeBits() and !data.payload.hasRuntimeBits()) {
3330 return AbiSizeAdvanced{ .scalar = 0 };
3331 } else if (!data.error_set.hasRuntimeBits()) {
3332 return AbiSizeAdvanced{ .scalar = data.payload.abiSize(target) };
3333 } else if (!data.payload.hasRuntimeBits()) {
3334 return AbiSizeAdvanced{ .scalar = data.error_set.abiSize(target) };
3382 // Here we need to care whether or not the error set is *empty* or whether
3383 // it only has *one possible value*. In the former case, it means there
3384 // cannot possibly be an error, meaning the ABI size is equivalent to the
3385 // payload ABI size. In the latter case, we need to account for the "tag"
3386 // because even if both the payload type and the error set type of an
3387 // error union have no runtime bits, an error union still has
3388 // 1 bit of data which is whether or not the value is an error.
3389 // Zig still uses the error code encoding at runtime, even when only 1 bit
3390 // would suffice. This prevents coercions from needing to branch.
3391 if (data.error_set.errorSetCardinality() == .zero) {
3392 return abiSizeAdvanced(data.payload, target, strat);
3393 }
3394 const code_size = abiSize(Type.anyerror, target);
3395 if (!data.payload.hasRuntimeBits()) {
3396 // Same as anyerror.
3397 return AbiSizeAdvanced{ .scalar = code_size };
33353398 }
3336 const code_align = abiAlignment(data.error_set, target);
3399 const code_align = abiAlignment(Type.anyerror, target);
33373400 const payload_align = abiAlignment(data.payload, target);
3338 const big_align = @maximum(code_align, payload_align);
33393401 const payload_size = abiSize(data.payload, target);
33403402
33413403 var size: u64 = 0;
3342 size += abiSize(data.error_set, target);
3343 size = std.mem.alignForwardGeneric(u64, size, payload_align);
3344 size += payload_size;
3345 size = std.mem.alignForwardGeneric(u64, size, big_align);
3404 if (code_align > payload_align) {
3405 size += code_size;
3406 size = std.mem.alignForwardGeneric(u64, size, payload_align);
3407 size += payload_size;
3408 size = std.mem.alignForwardGeneric(u64, size, code_align);
3409 } else {
3410 size += payload_size;
3411 size = std.mem.alignForwardGeneric(u64, size, code_align);
3412 size += code_size;
3413 size = std.mem.alignForwardGeneric(u64, size, payload_align);
3414 }
33463415 return AbiSizeAdvanced{ .scalar = size };
33473416 },
33483417 }
......@@ -4166,6 +4235,35 @@ pub const Type = extern union {
41664235 };
41674236 }
41684237
4238 const ErrorSetCardinality = enum { zero, one, many };
4239
4240 pub fn errorSetCardinality(ty: Type) ErrorSetCardinality {
4241 switch (ty.tag()) {
4242 .anyerror => return .many,
4243 .error_set_inferred => return .many,
4244 .error_set_single => return .one,
4245 .error_set => {
4246 const err_set_obj = ty.castTag(.error_set).?.data;
4247 const names = err_set_obj.names.keys();
4248 switch (names.len) {
4249 0 => return .zero,
4250 1 => return .one,
4251 else => return .many,
4252 }
4253 },
4254 .error_set_merged => {
4255 const name_map = ty.castTag(.error_set_merged).?.data;
4256 const names = name_map.keys();
4257 switch (names.len) {
4258 0 => return .zero,
4259 1 => return .one,
4260 else => return .many,
4261 }
4262 },
4263 else => unreachable,
4264 }
4265 }
4266
41694267 /// Returns true if it is an error set that includes anyerror, false otherwise.
41704268 /// Note that the result may be a false negative if the type did not get error set
41714269 /// resolution prior to this call.
......@@ -4664,10 +4762,7 @@ pub const Type = extern union {
46644762 .enum_literal,
46654763 .anyerror_void_error_union,
46664764 .error_union,
4667 .error_set,
4668 .error_set_single,
46694765 .error_set_inferred,
4670 .error_set_merged,
46714766 .@"opaque",
46724767 .var_args_param,
46734768 .manyptr_u8,
......@@ -4696,6 +4791,18 @@ pub const Type = extern union {
46964791 .bound_fn,
46974792 => return null,
46984793
4794 .error_set_single => return Value.initTag(.the_only_possible_value),
4795 .error_set => {
4796 const err_set_obj = ty.castTag(.error_set).?.data;
4797 if (err_set_obj.names.count() > 1) return null;
4798 return Value.initTag(.the_only_possible_value);
4799 },
4800 .error_set_merged => {
4801 const name_map = ty.castTag(.error_set_merged).?.data;
4802 if (name_map.count() > 1) return null;
4803 return Value.initTag(.the_only_possible_value);
4804 },
4805
46994806 .@"struct" => {
47004807 const s = ty.castTag(.@"struct").?.data;
47014808 assert(s.haveFieldTypes());
test/behavior/error.zig+28-3
......@@ -148,18 +148,39 @@ test "implicit cast to optional to error union to return result loc" {
148148 //comptime S.entry(); TODO
149149}
150150
151test "error: fn returning empty error set can be passed as fn returning any error" {
151test "fn returning empty error set can be passed as fn returning any error" {
152152 entry();
153153 comptime entry();
154154}
155155
156test "fn returning empty error set can be passed as fn returning any error - pointer" {
157 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
158
159 entryPtr();
160 comptime entryPtr();
161}
162
156163fn entry() void {
157164 foo2(bar2);
158165}
159166
167fn entryPtr() void {
168 var ptr = &bar2;
169 fooPtr(ptr);
170}
171
160172fn foo2(f: fn () anyerror!void) void {
161173 const x = f();
162 x catch {};
174 x catch {
175 @panic("fail");
176 };
177}
178
179fn fooPtr(f: *const fn () anyerror!void) void {
180 const x = f();
181 x catch {
182 @panic("fail");
183 };
163184}
164185
165186fn bar2() (error{}!void) {}
......@@ -239,7 +260,11 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
239260}
240261
241262test "comptime err to int of error set with only 1 possible value" {
242 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
263 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
264 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
265 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
266 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
267 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
243268
244269 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
245270 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));