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 {...@@ -1798,7 +1798,7 @@ fn resetSegfaultHandler() void {
1798 .mask = os.empty_sigset,1798 .mask = os.empty_sigset,
1799 .flags = 0,1799 .flags = 0,
1800 };1800 };
1801 // do nothing if an error happens to avoid a double-panic1801 // To avoid a double-panic, do nothing if an error happens here.
1802 updateSegfaultHandler(&act) catch {};1802 updateSegfaultHandler(&act) catch {};
1803}1803}
18041804
src/Sema.zig+49-14
...@@ -5899,12 +5899,22 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -5899,12 +5899,22 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
5899 if (val.isUndef()) {5899 if (val.isUndef()) {
5900 return sema.addConstUndef(result_ty);5900 return sema.addConstUndef(result_ty);
5901 }5901 }
5902 const payload = try sema.arena.create(Value.Payload.U64);5902 switch (val.tag()) {
5903 payload.* = .{5903 .@"error" => {
5904 .base = .{ .tag = .int_u64 },5904 const payload = try sema.arena.create(Value.Payload.U64);
5905 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,5905 payload.* = .{
5906 };5906 .base = .{ .tag = .int_u64 },
5907 return sema.addConstant(result_ty, Value.initPayload(&payload.base));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 }
5908 }5918 }
59095919
5910 try sema.requireRuntimeBlock(block, src);5920 try sema.requireRuntimeBlock(block, src);
...@@ -6261,19 +6271,24 @@ fn zirErrUnionPayload(...@@ -6261,19 +6271,24 @@ fn zirErrUnionPayload(
6261 });6271 });
6262 }6272 }
62636273
6274 const result_ty = operand_ty.errorUnionPayload();
6264 if (try sema.resolveDefinedValue(block, src, operand)) |val| {6275 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
6265 if (val.getError()) |name| {6276 if (val.getError()) |name| {
6266 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});6277 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
6267 }6278 }
6268 const data = val.castTag(.eu_payload).?.data;6279 const data = val.castTag(.eu_payload).?.data;
6269 const result_ty = operand_ty.errorUnionPayload();
6270 return sema.addConstant(result_ty, data);6280 return sema.addConstant(result_ty, data);
6271 }6281 }
6282
6272 try sema.requireRuntimeBlock(block, src);6283 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 {
6274 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);6289 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
6275 }6290 }
6276 const result_ty = operand_ty.errorUnionPayload();6291
6277 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);6292 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
6278}6293}
62796294
...@@ -6311,7 +6326,8 @@ fn analyzeErrUnionPayloadPtr(...@@ -6311,7 +6326,8 @@ fn analyzeErrUnionPayloadPtr(
6311 });6326 });
6312 }6327 }
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();
6315 const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{6331 const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{
6316 .pointee_type = payload_ty,6332 .pointee_type = payload_ty,
6317 .mutable = !operand_ty.isConstPtr(),6333 .mutable = !operand_ty.isConstPtr(),
...@@ -6351,9 +6367,14 @@ fn analyzeErrUnionPayloadPtr(...@@ -6351,9 +6367,14 @@ fn analyzeErrUnionPayloadPtr(
6351 }6367 }
63526368
6353 try sema.requireRuntimeBlock(block, src);6369 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 {
6355 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);6375 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
6356 }6376 }
6377
6357 const air_tag: Air.Inst.Tag = if (initializing)6378 const air_tag: Air.Inst.Tag = if (initializing)
6358 .errunion_payload_ptr_set6379 .errunion_payload_ptr_set
6359 else6380 else
...@@ -23301,10 +23322,7 @@ pub fn typeHasOnePossibleValue(...@@ -23301,10 +23322,7 @@ pub fn typeHasOnePossibleValue(
23301 .enum_literal,23322 .enum_literal,
23302 .anyerror_void_error_union,23323 .anyerror_void_error_union,
23303 .error_union,23324 .error_union,
23304 .error_set,
23305 .error_set_single,
23306 .error_set_inferred,23325 .error_set_inferred,
23307 .error_set_merged,
23308 .@"opaque",23326 .@"opaque",
23309 .var_args_param,23327 .var_args_param,
23310 .manyptr_u8,23328 .manyptr_u8,
...@@ -23333,6 +23351,23 @@ pub fn typeHasOnePossibleValue(...@@ -23333,6 +23351,23 @@ pub fn typeHasOnePossibleValue(
23333 .bound_fn,23351 .bound_fn,
23334 => return null,23352 => 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
23336 .@"struct" => {23371 .@"struct" => {
23337 const resolved_ty = try sema.resolveTypeFields(block, src, ty);23372 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
23338 const s = resolved_ty.castTag(.@"struct").?.data;23373 const s = resolved_ty.castTag(.@"struct").?.data;
src/codegen/llvm.zig+139-61
...@@ -2451,20 +2451,22 @@ pub const DeclGen = struct {...@@ -2451,20 +2451,22 @@ pub const DeclGen = struct {
2451 .ErrorUnion => {2451 .ErrorUnion => {
2452 const error_type = t.errorUnionSet();2452 const error_type = t.errorUnionSet();
2453 const payload_type = t.errorUnionPayload();2453 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 }
2455 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {2457 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
2456 return llvm_error_type;2458 return try dg.llvmType(Type.anyerror);
2457 }2459 }
2460 const llvm_error_type = try dg.llvmType(error_type);
2458 const llvm_payload_type = try dg.llvmType(payload_type);2461 const llvm_payload_type = try dg.llvmType(payload_type);
24592462
2460 const payload_align = payload_type.abiAlignment(target);2463 const payload_align = payload_type.abiAlignment(target);
2461 const error_size = error_type.abiSize(target);2464 const error_align = Type.anyerror.abiAlignment(target);
2462 if (payload_align > error_size) {2465 if (error_align > payload_align) {
2463 const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size));2466 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
2464 const fields: [3]*const llvm.Type = .{ llvm_error_type, pad_type, llvm_payload_type };
2465 return dg.context.structType(&fields, fields.len, .False);2467 return dg.context.structType(&fields, fields.len, .False);
2466 } else {2468 } 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 };
2468 return dg.context.structType(&fields, fields.len, .False);2470 return dg.context.structType(&fields, fields.len, .False);
2469 }2471 }
2470 },2472 },
...@@ -3103,6 +3105,10 @@ pub const DeclGen = struct {...@@ -3103,6 +3105,10 @@ pub const DeclGen = struct {
3103 .ErrorUnion => {3105 .ErrorUnion => {
3104 const error_type = tv.ty.errorUnionSet();3106 const error_type = tv.ty.errorUnionSet();
3105 const payload_type = tv.ty.errorUnionPayload();3107 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 }
3106 const is_pl = tv.val.errorUnionIsPayload();3112 const is_pl = tv.val.errorUnionIsPayload();
31073113
3108 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {3114 if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
...@@ -3110,28 +3116,24 @@ pub const DeclGen = struct {...@@ -3110,28 +3116,24 @@ pub const DeclGen = struct {
3110 const err_val = if (!is_pl) tv.val else Value.initTag(.zero);3116 const err_val = if (!is_pl) tv.val else Value.initTag(.zero);
3111 return dg.genTypedValue(.{ .ty = error_type, .val = err_val });3117 return dg.genTypedValue(.{ .ty = error_type, .val = err_val });
3112 }3118 }
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
3126 const payload_align = payload_type.abiAlignment(target);3120 const payload_align = payload_type.abiAlignment(target);
3127 const error_size = error_type.abiSize(target);3121 const error_align = Type.anyerror.abiAlignment(target);
3128 if (payload_align > error_size) {3122 const llvm_error_value = try dg.genTypedValue(.{
3129 fields[2] = fields[1];3123 .ty = error_type,
3130 const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size));3124 .val = if (is_pl) Value.initTag(.zero) else tv.val,
3131 fields[1] = pad_type.getUndef();3125 });
3132 len += 1;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);
3133 }3136 }
3134 return dg.context.constStruct(&fields, len, .False);
3135 },3137 },
3136 .Struct => {3138 .Struct => {
3137 const llvm_struct_ty = try dg.llvmType(tv.ty);3139 const llvm_struct_ty = try dg.llvmType(tv.ty);
...@@ -4338,11 +4340,19 @@ pub const FuncGen = struct {...@@ -4338,11 +4340,19 @@ pub const FuncGen = struct {
4338 _ = self.builder.buildRetVoid();4340 _ = self.builder.buildRetVoid();
4339 return null;4341 return null;
4340 }4342 }
4343 const fn_info = self.dg.decl.ty.fnInfo();
4341 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {4344 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 }
4343 return null;4354 return null;
4344 }4355 }
4345 const fn_info = self.dg.decl.ty.fnInfo();
4346 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);4356 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
4347 const operand = try self.resolveInst(un_op);4357 const operand = try self.resolveInst(un_op);
4348 const llvm_ret_ty = operand.typeOf();4358 const llvm_ret_ty = operand.typeOf();
...@@ -4369,13 +4379,25 @@ pub const FuncGen = struct {...@@ -4369,13 +4379,25 @@ pub const FuncGen = struct {
4369 const un_op = self.air.instructions.items(.data)[inst].un_op;4379 const un_op = self.air.instructions.items(.data)[inst].un_op;
4370 const ptr_ty = self.air.typeOf(un_op);4380 const ptr_ty = self.air.typeOf(un_op);
4371 const ret_ty = ptr_ty.childType();4381 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) {
4373 _ = self.builder.buildRetVoid();4396 _ = self.builder.buildRetVoid();
4374 return null;4397 return null;
4375 }4398 }
4376 const ptr = try self.resolveInst(un_op);4399 const ptr = try self.resolveInst(un_op);
4377 const target = self.dg.module.getTarget();4400 const target = self.dg.module.getTarget();
4378 const fn_info = self.dg.decl.ty.fnInfo();
4379 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);4401 const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
4380 const llvm_ret_ty = try self.dg.llvmType(ret_ty);4402 const llvm_ret_ty = try self.dg.llvmType(ret_ty);
4381 const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: {4403 const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: {
...@@ -5433,18 +5455,30 @@ pub const FuncGen = struct {...@@ -5433,18 +5455,30 @@ pub const FuncGen = struct {
5433 const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror));5455 const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror));
5434 const zero = err_set_ty.constNull();5456 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
5436 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5467 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5437 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;5468 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
5438 return self.builder.buildICmp(op, loaded, zero, "");5469 return self.builder.buildICmp(op, loaded, zero, "");
5439 }5470 }
54405471
5472 const target = self.dg.module.getTarget();
5473 const err_field_index = errUnionErrorOffset(payload_ty, target);
5474
5441 if (operand_is_ptr or isByRef(err_union_ty)) {5475 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, "");
5443 const loaded = self.builder.buildLoad(err_field_ptr, "");5477 const loaded = self.builder.buildLoad(err_field_ptr, "");
5444 return self.builder.buildICmp(op, loaded, zero, "");5478 return self.builder.buildICmp(op, loaded, zero, "");
5445 }5479 }
54465480
5447 const loaded = self.builder.buildExtractValue(operand, 0, "");5481 const loaded = self.builder.buildExtractValue(operand, err_field_index, "");
5448 return self.builder.buildICmp(op, loaded, zero, "");5482 return self.builder.buildICmp(op, loaded, zero, "");
5449 }5483 }
54505484
...@@ -5544,11 +5578,17 @@ pub const FuncGen = struct {...@@ -5544,11 +5578,17 @@ pub const FuncGen = struct {
55445578
5545 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5579 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5546 const operand = try self.resolveInst(ty_op.operand);5580 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);
5548 const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;5589 const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
5549
5550 const target = self.dg.module.getTarget();5590 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
5553 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5593 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5554 if (!operand_is_ptr) return null;5594 if (!operand_is_ptr) return null;
...@@ -5574,54 +5614,70 @@ pub const FuncGen = struct {...@@ -5574,54 +5614,70 @@ pub const FuncGen = struct {
5574 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5614 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5575 const operand = try self.resolveInst(ty_op.operand);5615 const operand = try self.resolveInst(ty_op.operand);
5576 const operand_ty = self.air.typeOf(ty_op.operand);5616 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();
5580 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5628 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5581 if (!operand_is_ptr) return operand;5629 if (!operand_is_ptr) return operand;
5582 return self.builder.buildLoad(operand, "");5630 return self.builder.buildLoad(operand, "");
5583 }5631 }
55845632
5585 if (operand_is_ptr or isByRef(err_set_ty)) {5633 const target = self.dg.module.getTarget();
5586 const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");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, "");
5587 return self.builder.buildLoad(err_field_ptr, "");5638 return self.builder.buildLoad(err_field_ptr, "");
5588 }5639 }
55895640
5590 return self.builder.buildExtractValue(operand, 0, "");5641 return self.builder.buildExtractValue(operand, offset, "");
5591 }5642 }
55925643
5593 fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5644 fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
5594 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5645 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5595 const operand = try self.resolveInst(ty_op.operand);5646 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();5649 const error_ty = error_union_ty.errorUnionSet();
5599 const payload_ty = error_set_ty.errorUnionPayload();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();
5600 const non_error_val = try self.dg.genTypedValue(.{ .ty = error_ty, .val = Value.zero });5655 const non_error_val = try self.dg.genTypedValue(.{ .ty = error_ty, .val = Value.zero });
5601 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5656 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.
5603 _ = self.builder.buildStore(non_error_val, operand);5657 _ = self.builder.buildStore(non_error_val, operand);
5604 return operand;5658 return operand;
5605 }5659 }
5606 const index_type = self.context.intType(32);5660 const index_type = self.context.intType(32);
5661 const target = self.dg.module.getTarget();
5607 {5662 {
5663 const error_offset = errUnionErrorOffset(payload_ty, target);
5608 // First set the non-error value.5664 // First set the non-error value.
5609 const indices: [2]*const llvm.Value = .{5665 const indices: [2]*const llvm.Value = .{
5610 index_type.constNull(), // dereference the pointer5666 index_type.constNull(), // dereference the pointer
5611 index_type.constNull(), // first field is the payload5667 index_type.constInt(error_offset, .False),
5612 };5668 };
5613 const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");5669 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));
5615 }5672 }
5616 // Then return the payload pointer (only if it is used).5673 // Then return the payload pointer (only if it is used).
5617 if (self.liveness.isUnused(inst))5674 if (self.liveness.isUnused(inst))
5618 return null;5675 return null;
56195676
5620 const target = self.dg.module.getTarget();5677 const payload_offset = errUnionPayloadOffset(payload_ty, target);
5621 const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1;
5622 const indices: [2]*const llvm.Value = .{5678 const indices: [2]*const llvm.Value = .{
5623 index_type.constNull(), // dereference the pointer5679 index_type.constNull(), // dereference the pointer
5624 index_type.constInt(payload_offset, .False), // second field is the payload5680 index_type.constInt(payload_offset, .False),
5625 };5681 };
5626 return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");5682 return self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
5627 }5683 }
...@@ -5669,21 +5725,26 @@ pub const FuncGen = struct {...@@ -5669,21 +5725,26 @@ pub const FuncGen = struct {
5669 if (self.liveness.isUnused(inst)) return null;5725 if (self.liveness.isUnused(inst)) return null;
56705726
5671 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5727 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);
5673 const operand = try self.resolveInst(ty_op.operand);5729 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);
5674 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {5734 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
5675 return operand;5735 return operand;
5676 }5736 }
5677 const inst_ty = self.air.typeOfIndex(inst);5737 const ok_err_code = (try self.dg.llvmType(Type.anyerror)).constNull();
5678 const ok_err_code = self.context.intType(16).constNull();
5679 const err_un_llvm_ty = try self.dg.llvmType(inst_ty);5738 const err_un_llvm_ty = try self.dg.llvmType(inst_ty);
56805739
5681 const target = self.dg.module.getTarget();5740 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);
5683 if (isByRef(inst_ty)) {5743 if (isByRef(inst_ty)) {
5684 const result_ptr = self.buildAlloca(err_un_llvm_ty);5744 const result_ptr = self.buildAlloca(err_un_llvm_ty);
5685 const err_ptr = self.builder.buildStructGEP(result_ptr, 0, "");5745 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");
5686 _ = self.builder.buildStore(ok_err_code, err_ptr);5746 const store_inst = self.builder.buildStore(ok_err_code, err_ptr);
5747 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
5687 const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, "");5748 const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, "");
5688 var ptr_ty_payload: Type.Payload.ElemType = .{5749 var ptr_ty_payload: Type.Payload.ElemType = .{
5689 .base = .{ .tag = .single_mut_pointer },5750 .base = .{ .tag = .single_mut_pointer },
...@@ -5694,7 +5755,7 @@ pub const FuncGen = struct {...@@ -5694,7 +5755,7 @@ pub const FuncGen = struct {
5694 return result_ptr;5755 return result_ptr;
5695 }5756 }
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, "");
5698 return self.builder.buildInsertValue(partial, operand, payload_offset, "");5759 return self.builder.buildInsertValue(partial, operand, payload_offset, "");
5699 }5760 }
57005761
...@@ -5711,11 +5772,13 @@ pub const FuncGen = struct {...@@ -5711,11 +5772,13 @@ pub const FuncGen = struct {
5711 const err_un_llvm_ty = try self.dg.llvmType(err_un_ty);5772 const err_un_llvm_ty = try self.dg.llvmType(err_un_ty);
57125773
5713 const target = self.dg.module.getTarget();5774 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);
5715 if (isByRef(err_un_ty)) {5777 if (isByRef(err_un_ty)) {
5716 const result_ptr = self.buildAlloca(err_un_llvm_ty);5778 const result_ptr = self.buildAlloca(err_un_llvm_ty);
5717 const err_ptr = self.builder.buildStructGEP(result_ptr, 0, "");5779 const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, "");
5718 _ = self.builder.buildStore(operand, err_ptr);5780 const store_inst = self.builder.buildStore(operand, err_ptr);
5781 store_inst.setAlignment(Type.anyerror.abiAlignment(target));
5719 const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, "");5782 const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, "");
5720 var ptr_ty_payload: Type.Payload.ElemType = .{5783 var ptr_ty_payload: Type.Payload.ElemType = .{
5721 .base = .{ .tag = .single_mut_pointer },5784 .base = .{ .tag = .single_mut_pointer },
...@@ -5728,7 +5791,7 @@ pub const FuncGen = struct {...@@ -5728,7 +5791,7 @@ pub const FuncGen = struct {
5728 return result_ptr;5791 return result_ptr;
5729 }5792 }
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, "");
5732 // TODO set payload bytes to undef5795 // TODO set payload bytes to undef
5733 return partial;5796 return partial;
5734 }5797 }
...@@ -8546,7 +8609,14 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -8546,7 +8609,14 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
8546/// be effectively bitcasted to the actual return type.8609/// be effectively bitcasted to the actual return type.
8547fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.Type {8610fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.Type {
8548 if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) {8611 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 }
8550 }8620 }
8551 const target = dg.module.getTarget();8621 const target = dg.module.getTarget();
8552 switch (fn_info.cc) {8622 switch (fn_info.cc) {
...@@ -8991,3 +9061,11 @@ fn buildAllocaInner(...@@ -8991,3 +9061,11 @@ fn buildAllocaInner(
89919061
8992 return builder.buildAlloca(llvm_ty, "");9062 return builder.buildAlloca(llvm_ty, "");
8993}9063}
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 {...@@ -2317,10 +2317,7 @@ pub const Type = extern union {
2317 .const_slice_u8_sentinel_0,2317 .const_slice_u8_sentinel_0,
2318 .array_u8_sentinel_0,2318 .array_u8_sentinel_0,
2319 .anyerror_void_error_union,2319 .anyerror_void_error_union,
2320 .error_set,
2321 .error_set_single,
2322 .error_set_inferred,2320 .error_set_inferred,
2323 .error_set_merged,
2324 .manyptr_u8,2321 .manyptr_u8,
2325 .manyptr_const_u8,2322 .manyptr_const_u8,
2326 .manyptr_const_u8_sentinel_0,2323 .manyptr_const_u8_sentinel_0,
...@@ -2361,8 +2358,20 @@ pub const Type = extern union {...@@ -2361,8 +2358,20 @@ pub const Type = extern union {
2361 .fn_void_no_args,2358 .fn_void_no_args,
2362 .fn_naked_noreturn_no_args,2359 .fn_naked_noreturn_no_args,
2363 .fn_ccc_void_no_args,2360 .fn_ccc_void_no_args,
2361 .error_set_single,
2364 => return false,2362 => 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
2366 // These types have more than one possible value, so the result is the same as2375 // These types have more than one possible value, so the result is the same as
2367 // asking whether they are comptime-only types.2376 // asking whether they are comptime-only types.
2368 .anyframe_T,2377 .anyframe_T,
...@@ -2388,6 +2397,21 @@ pub const Type = extern union {...@@ -2388,6 +2397,21 @@ pub const Type = extern union {
2388 }2397 }
2389 },2398 },
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
2391 .@"struct" => {2415 .@"struct" => {
2392 const struct_obj = ty.castTag(.@"struct").?.data;2416 const struct_obj = ty.castTag(.@"struct").?.data;
2393 if (sema_kit) |sk| {2417 if (sema_kit) |sk| {
...@@ -2467,12 +2491,6 @@ pub const Type = extern union {...@@ -2467,12 +2491,6 @@ pub const Type = extern union {
24672491
2468 .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0,2492 .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
2476 .tuple, .anon_struct => {2494 .tuple, .anon_struct => {
2477 const tuple = ty.tupleFields();2495 const tuple = ty.tupleFields();
2478 for (tuple.types) |field_ty, i| {2496 for (tuple.types) |field_ty, i| {
...@@ -2852,13 +2870,30 @@ pub const Type = extern union {...@@ -2852,13 +2870,30 @@ pub const Type = extern union {
2852 else => unreachable,2870 else => unreachable,
2853 },2871 },
28542872
2855 .error_set,2873 // TODO revisit this when we have the concept of the error tag type
2856 .error_set_single,
2857 .anyerror_void_error_union,2874 .anyerror_void_error_union,
2858 .anyerror,2875 .anyerror,
2859 .error_set_inferred,2876 .error_set_inferred,
2860 .error_set_merged,2877 => return AbiAlignmentAdvanced{ .scalar = 2 },
2861 => return AbiAlignmentAdvanced{ .scalar = 2 }, // TODO revisit this when we have the concept of the error tag type2878
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
2863 .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat),2898 .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat),
28642899
...@@ -2900,31 +2935,29 @@ pub const Type = extern union {...@@ -2900,31 +2935,29 @@ pub const Type = extern union {
2900 },2935 },
29012936
2902 .error_union => {2937 .error_union => {
2938 // This code needs to be kept in sync with the equivalent switch prong
2939 // in abiSizeAdvanced.
2903 const data = ty.castTag(.error_union).?.data;2940 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);
2904 switch (strat) {2945 switch (strat) {
2905 .eager, .sema_kit => {2946 .eager, .sema_kit => {
2906 if (!(try data.error_set.hasRuntimeBitsAdvanced(false, sema_kit))) {2947 if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) {
2907 return data.payload.abiAlignmentAdvanced(target, strat);2948 return AbiAlignmentAdvanced{ .scalar = code_align };
2908 } else if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) {
2909 return data.error_set.abiAlignmentAdvanced(target, strat);
2910 }2949 }
2911 return AbiAlignmentAdvanced{ .scalar = @maximum(2950 return AbiAlignmentAdvanced{ .scalar = @maximum(
2951 code_align,
2912 (try data.payload.abiAlignmentAdvanced(target, strat)).scalar,2952 (try data.payload.abiAlignmentAdvanced(target, strat)).scalar,
2913 (try data.error_set.abiAlignmentAdvanced(target, strat)).scalar,
2914 ) };2953 ) };
2915 },2954 },
2916 .lazy => |arena| {2955 .lazy => |arena| {
2917 switch (try data.payload.abiAlignmentAdvanced(target, strat)) {2956 switch (try data.payload.abiAlignmentAdvanced(target, strat)) {
2918 .scalar => |payload_align| {2957 .scalar => |payload_align| {
2919 if (payload_align == 0) {2958 return AbiAlignmentAdvanced{
2920 return data.error_set.abiAlignmentAdvanced(target, strat);2959 .scalar = @maximum(code_align, payload_align),
2921 }2960 };
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 }
2928 },2961 },
2929 .val => {},2962 .val => {},
2930 }2963 }
...@@ -3018,6 +3051,7 @@ pub const Type = extern union {...@@ -3018,6 +3051,7 @@ pub const Type = extern union {
3018 .@"undefined",3051 .@"undefined",
3019 .enum_literal,3052 .enum_literal,
3020 .type_info,3053 .type_info,
3054 .error_set_single,
3021 => return AbiAlignmentAdvanced{ .scalar = 0 },3055 => return AbiAlignmentAdvanced{ .scalar = 0 },
30223056
3023 .noreturn,3057 .noreturn,
...@@ -3136,6 +3170,7 @@ pub const Type = extern union {...@@ -3136,6 +3170,7 @@ pub const Type = extern union {
3136 .empty_struct_literal,3170 .empty_struct_literal,
3137 .empty_struct,3171 .empty_struct,
3138 .void,3172 .void,
3173 .error_set_single,
3139 => return AbiSizeAdvanced{ .scalar = 0 },3174 => return AbiSizeAdvanced{ .scalar = 0 },
31403175
3141 .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) {3176 .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) {
...@@ -3291,14 +3326,30 @@ pub const Type = extern union {...@@ -3291,14 +3326,30 @@ pub const Type = extern union {
3291 },3326 },
32923327
3293 // TODO revisit this when we have the concept of the error tag type3328 // TODO revisit this when we have the concept of the error tag type
3294 .error_set,
3295 .error_set_single,
3296 .anyerror_void_error_union,3329 .anyerror_void_error_union,
3297 .anyerror,3330 .anyerror,
3298 .error_set_inferred,3331 .error_set_inferred,
3299 .error_set_merged,
3300 => return AbiSizeAdvanced{ .scalar = 2 },3332 => 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
3302 .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) },3353 .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) },
3303 .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) },3354 .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) },
3304 .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) },3355 .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) },
...@@ -3325,24 +3376,42 @@ pub const Type = extern union {...@@ -3325,24 +3376,42 @@ pub const Type = extern union {
3325 },3376 },
33263377
3327 .error_union => {3378 .error_union => {
3379 // This code needs to be kept in sync with the equivalent switch prong
3380 // in abiAlignmentAdvanced.
3328 const data = ty.castTag(.error_union).?.data;3381 const data = ty.castTag(.error_union).?.data;
3329 if (!data.error_set.hasRuntimeBits() and !data.payload.hasRuntimeBits()) {3382 // Here we need to care whether or not the error set is *empty* or whether
3330 return AbiSizeAdvanced{ .scalar = 0 };3383 // it only has *one possible value*. In the former case, it means there
3331 } else if (!data.error_set.hasRuntimeBits()) {3384 // cannot possibly be an error, meaning the ABI size is equivalent to the
3332 return AbiSizeAdvanced{ .scalar = data.payload.abiSize(target) };3385 // payload ABI size. In the latter case, we need to account for the "tag"
3333 } else if (!data.payload.hasRuntimeBits()) {3386 // because even if both the payload type and the error set type of an
3334 return AbiSizeAdvanced{ .scalar = data.error_set.abiSize(target) };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 };
3335 }3398 }
3336 const code_align = abiAlignment(data.error_set, target);3399 const code_align = abiAlignment(Type.anyerror, target);
3337 const payload_align = abiAlignment(data.payload, target);3400 const payload_align = abiAlignment(data.payload, target);
3338 const big_align = @maximum(code_align, payload_align);
3339 const payload_size = abiSize(data.payload, target);3401 const payload_size = abiSize(data.payload, target);
33403402
3341 var size: u64 = 0;3403 var size: u64 = 0;
3342 size += abiSize(data.error_set, target);3404 if (code_align > payload_align) {
3343 size = std.mem.alignForwardGeneric(u64, size, payload_align);3405 size += code_size;
3344 size += payload_size;3406 size = std.mem.alignForwardGeneric(u64, size, payload_align);
3345 size = std.mem.alignForwardGeneric(u64, size, big_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 }
3346 return AbiSizeAdvanced{ .scalar = size };3415 return AbiSizeAdvanced{ .scalar = size };
3347 },3416 },
3348 }3417 }
...@@ -4166,6 +4235,35 @@ pub const Type = extern union {...@@ -4166,6 +4235,35 @@ pub const Type = extern union {
4166 };4235 };
4167 }4236 }
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
4169 /// Returns true if it is an error set that includes anyerror, false otherwise.4267 /// Returns true if it is an error set that includes anyerror, false otherwise.
4170 /// Note that the result may be a false negative if the type did not get error set4268 /// Note that the result may be a false negative if the type did not get error set
4171 /// resolution prior to this call.4269 /// resolution prior to this call.
...@@ -4664,10 +4762,7 @@ pub const Type = extern union {...@@ -4664,10 +4762,7 @@ pub const Type = extern union {
4664 .enum_literal,4762 .enum_literal,
4665 .anyerror_void_error_union,4763 .anyerror_void_error_union,
4666 .error_union,4764 .error_union,
4667 .error_set,
4668 .error_set_single,
4669 .error_set_inferred,4765 .error_set_inferred,
4670 .error_set_merged,
4671 .@"opaque",4766 .@"opaque",
4672 .var_args_param,4767 .var_args_param,
4673 .manyptr_u8,4768 .manyptr_u8,
...@@ -4696,6 +4791,18 @@ pub const Type = extern union {...@@ -4696,6 +4791,18 @@ pub const Type = extern union {
4696 .bound_fn,4791 .bound_fn,
4697 => return null,4792 => 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
4699 .@"struct" => {4806 .@"struct" => {
4700 const s = ty.castTag(.@"struct").?.data;4807 const s = ty.castTag(.@"struct").?.data;
4701 assert(s.haveFieldTypes());4808 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" {...@@ -148,18 +148,39 @@ test "implicit cast to optional to error union to return result loc" {
148 //comptime S.entry(); TODO148 //comptime S.entry(); TODO
149}149}
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" {
152 entry();152 entry();
153 comptime entry();153 comptime entry();
154}154}
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
156fn entry() void {163fn entry() void {
157 foo2(bar2);164 foo2(bar2);
158}165}
159166
167fn entryPtr() void {
168 var ptr = &bar2;
169 fooPtr(ptr);
170}
171
160fn foo2(f: fn () anyerror!void) void {172fn foo2(f: fn () anyerror!void) void {
161 const x = f();173 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 };
163}184}
164185
165fn bar2() (error{}!void) {}186fn bar2() (error{}!void) {}
...@@ -239,7 +260,11 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {...@@ -239,7 +260,11 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
239}260}
240261
241test "comptime err to int of error set with only 1 possible value" {262test "comptime err to int of error set with only 1 possible value" {
242 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO263 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
244 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));269 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
245 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));270 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));