authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 15:10:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 15:34:52-07:00
logc711c788f0a840f45d0d7423efe2f946b47caafb
tree48f57fc696e485937a7b96477657bdd8cdaff534
parentc847a462ae11e0d483ad877b3ecc9ec291c29bb3

stage2: fixes for error unions, optionals, errors

* `?E` where E is an error set with only one field now lowers the same as `bool`. * Fix implementation of errUnionErrOffset and errUnionPayloadOffset to properly compute the offset of each field. Also name them the same as the corresponding LLVM functions and have the same function signature, to avoid confusion. This fixes a bug where wasm was passing the error union type instead of the payload type. * Fix C backend handling of optionals with zero-bit payload types. * C backend: separate out airOptionalPayload and airOptionalPayloadPtr which reduces branching and cleans up control flow. * Make Type.isNoReturn return true for error sets with no fields. * Make `?error{}` have only one possible value (null).

9 files changed, 191 insertions(+), 76 deletions(-)

src/Sema.zig+10-1
......@@ -23316,7 +23316,6 @@ pub fn typeHasOnePossibleValue(
2331623316 .const_slice,
2331723317 .mut_slice,
2331823318 .anyopaque,
23319 .optional,
2332023319 .optional_single_mut_pointer,
2332123320 .optional_single_const_pointer,
2332223321 .enum_literal,
......@@ -23351,6 +23350,16 @@ pub fn typeHasOnePossibleValue(
2335123350 .bound_fn,
2335223351 => return null,
2335323352
23353 .optional => {
23354 var buf: Type.Payload.ElemType = undefined;
23355 const child_ty = ty.optionalChild(&buf);
23356 if (child_ty.isNoReturn()) {
23357 return Value.@"null";
23358 } else {
23359 return null;
23360 }
23361 },
23362
2335423363 .error_set_single => {
2335523364 const name = ty.castTag(.error_set_single).?.data;
2335623365 return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
src/arch/aarch64/CodeGen.zig+2-2
......@@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
3030const bits = @import("bits.zig");
3131const abi = @import("abi.zig");
3232const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrOffset = codegen.errUnionErrOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434const RegisterManager = abi.RegisterManager;
3535const RegisterLock = RegisterManager.RegisterLock;
3636const Register = bits.Register;
......@@ -3615,7 +3615,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
36153615 return MCValue{ .immediate = 0 }; // always false
36163616 }
36173617
3618 const err_off = errUnionErrOffset(ty, self.target.*);
3618 const err_off = errUnionErrorOffset(payload_type, self.target.*);
36193619 switch (operand) {
36203620 .stack_offset => |off| {
36213621 const offset = off - @intCast(u32, err_off);
src/arch/arm/CodeGen.zig+3-3
......@@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
3030const bits = @import("bits.zig");
3131const abi = @import("abi.zig");
3232const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrOffset = codegen.errUnionErrOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434const RegisterManager = abi.RegisterManager;
3535const RegisterLock = RegisterManager.RegisterLock;
3636const Register = bits.Register;
......@@ -1775,7 +1775,7 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV
17751775 return error_union_mcv;
17761776 }
17771777
1778 const err_offset = @intCast(u32, errUnionErrOffset(error_union_ty, self.target.*));
1778 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
17791779 switch (error_union_mcv) {
17801780 .register => return self.fail("TODO errUnionErr for registers", .{}),
17811781 .stack_argument_offset => |off| {
......@@ -1812,7 +1812,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
18121812 return MCValue.none;
18131813 }
18141814
1815 const payload_offset = @intCast(u32, errUnionPayloadOffset(error_union_ty, self.target.*));
1815 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
18161816 switch (error_union_mcv) {
18171817 .register => return self.fail("TODO errUnionPayload for registers", .{}),
18181818 .stack_argument_offset => |off| {
src/arch/wasm/CodeGen.zig+9-9
......@@ -23,7 +23,7 @@ const Mir = @import("Mir.zig");
2323const Emit = @import("Emit.zig");
2424const abi = @import("abi.zig");
2525const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
26const errUnionErrOffset = codegen.errUnionErrOffset;
26const errUnionErrorOffset = codegen.errUnionErrorOffset;
2727
2828/// Wasm Value, created when generating an instruction
2929const WValue = union(enum) {
......@@ -2919,10 +2919,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29192919fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
29202920 const un_op = self.air.instructions.items(.data)[inst].un_op;
29212921 const operand = try self.resolveInst(un_op);
2922 const err_ty = self.air.typeOf(un_op);
2923 const pl_ty = err_ty.errorUnionPayload();
2922 const err_union_ty = self.air.typeOf(un_op);
2923 const pl_ty = err_union_ty.errorUnionPayload();
29242924
2925 if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
2925 if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
29262926 switch (opcode) {
29272927 .i32_ne => return WValue{ .imm32 = 0 },
29282928 .i32_eq => return WValue{ .imm32 = 1 },
......@@ -2933,7 +2933,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
29332933 try self.emitWValue(operand);
29342934 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
29352935 try self.addMemArg(.i32_load16_u, .{
2936 .offset = operand.offset() + @intCast(u32, errUnionErrOffset(pl_ty, self.target)),
2936 .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)),
29372937 .alignment = Type.anyerror.abiAlignment(self.target),
29382938 });
29392939 }
......@@ -2985,7 +2985,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
29852985 return operand;
29862986 }
29872987
2988 return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrOffset(payload_ty, self.target)));
2988 return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
29892989}
29902990
29912991fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3011,7 +3011,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30113011 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
30123012 try self.emitWValue(err_union);
30133013 try self.addImm32(0);
3014 const err_val_offset = @intCast(u32, errUnionErrOffset(pl_ty, self.target));
3014 const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
30153015 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
30163016
30173017 return err_union;
......@@ -3031,7 +3031,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30313031
30323032 const err_union = try self.allocStack(err_ty);
30333033 // store error value
3034 try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrOffset(pl_ty, self.target)));
3034 try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target)));
30353035
30363036 // write 'undefined' to the payload
30373037 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
......@@ -3986,7 +3986,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
39863986 operand,
39873987 .{ .imm32 = 0 },
39883988 Type.anyerror,
3989 @intCast(u32, errUnionErrOffset(payload_ty, self.target)),
3989 @intCast(u32, errUnionErrorOffset(payload_ty, self.target)),
39903990 );
39913991
39923992 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
src/arch/x86_64/CodeGen.zig+8-8
......@@ -30,7 +30,7 @@ const Value = @import("../../value.zig").Value;
3030const bits = @import("bits.zig");
3131const abi = @import("abi.zig");
3232const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrOffset = codegen.errUnionErrOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
3535const callee_preserved_regs = abi.callee_preserved_regs;
3636const caller_preserved_regs = abi.caller_preserved_regs;
......@@ -1799,7 +1799,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
17991799 break :result operand;
18001800 }
18011801
1802 const err_off = errUnionErrOffset(err_union_ty, self.target.*);
1802 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
18031803 switch (operand) {
18041804 .stack_offset => |off| {
18051805 const offset = off - @intCast(i32, err_off);
......@@ -1844,7 +1844,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
18441844 break :result MCValue.none;
18451845 }
18461846
1847 const payload_off = errUnionPayloadOffset(err_union_ty, self.target.*);
1847 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
18481848 switch (operand) {
18491849 .stack_offset => |off| {
18501850 const offset = off - @intCast(i32, payload_off);
......@@ -1978,8 +1978,8 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
19781978 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
19791979 const abi_align = error_union_ty.abiAlignment(self.target.*);
19801980 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
1981 const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*);
1982 const err_off = errUnionErrOffset(error_union_ty, self.target.*);
1981 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
1982 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
19831983 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{});
19841984 try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), .{ .immediate = 0 }, .{});
19851985
......@@ -2007,8 +2007,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
20072007 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
20082008 const abi_align = error_union_ty.abiAlignment(self.target.*);
20092009 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
2010 const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*);
2011 const err_off = errUnionErrOffset(error_union_ty, self.target.*);
2010 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
2011 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
20122012 try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{});
20132013 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), .undef, .{});
20142014
......@@ -4670,7 +4670,7 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
46704670 try self.spillCompareFlagsIfOccupied();
46714671 self.compare_flags_inst = inst;
46724672
4673 const err_off = errUnionErrOffset(ty, self.target.*);
4673 const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);
46744674 switch (operand) {
46754675 .stack_offset => |off| {
46764676 const offset = off - @intCast(i32, err_off);
src/codegen.zig+16-12
......@@ -891,18 +891,22 @@ fn lowerDeclRef(
891891 return Result{ .appended = {} };
892892}
893893
894pub fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 {
895 const payload_ty = ty.errorUnionPayload();
896 return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target))
897 Type.anyerror.abiSize(target)
898 else
899 0;
894pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
895 const payload_align = payload_ty.abiAlignment(target);
896 const error_align = Type.anyerror.abiAlignment(target);
897 if (payload_align >= error_align) {
898 return 0;
899 } else {
900 return mem.alignForwardGeneric(u64, Type.anyerror.abiSize(target), payload_align);
901 }
900902}
901903
902pub fn errUnionErrOffset(ty: Type, target: std.Target) u64 {
903 const payload_ty = ty.errorUnionPayload();
904 return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target))
905 0
906 else
907 payload_ty.abiSize(target);
904pub fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u64 {
905 const payload_align = payload_ty.abiAlignment(target);
906 const error_align = Type.anyerror.abiAlignment(target);
907 if (payload_align >= error_align) {
908 return mem.alignForwardGeneric(u64, payload_ty.abiSize(target), error_align);
909 } else {
910 return 0;
911 }
908912}
src/codegen/c.zig+56-30
......@@ -711,21 +711,24 @@ pub const DeclGen = struct {
711711 .Bool => return writer.print("{}", .{val.toBool()}),
712712 .Optional => {
713713 var opt_buf: Type.Payload.ElemType = undefined;
714 const payload_type = ty.optionalChild(&opt_buf);
715 if (ty.optionalReprIsPayload()) {
716 return dg.renderValue(writer, payload_type, val, location);
717 }
718 if (payload_type.abiSize(target) == 0) {
714 const payload_ty = ty.optionalChild(&opt_buf);
715
716 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
719717 const is_null = val.castTag(.opt_payload) == null;
720718 return writer.print("{}", .{is_null});
721719 }
720
721 if (ty.optionalReprIsPayload()) {
722 return dg.renderValue(writer, payload_ty, val, location);
723 }
724
722725 try writer.writeByte('(');
723726 try dg.renderTypecast(writer, ty);
724727 try writer.writeAll("){");
725728 if (val.castTag(.opt_payload)) |pl| {
726729 const payload_val = pl.data;
727730 try writer.writeAll(" .is_null = false, .payload = ");
728 try dg.renderValue(writer, payload_type, payload_val, location);
731 try dg.renderValue(writer, payload_ty, payload_val, location);
729732 try writer.writeAll(" }");
730733 } else {
731734 try writer.writeAll(" .is_null = true }");
......@@ -1360,12 +1363,12 @@ pub const DeclGen = struct {
13601363 var opt_buf: Type.Payload.ElemType = undefined;
13611364 const child_type = t.optionalChild(&opt_buf);
13621365
1363 if (t.optionalReprIsPayload()) {
1364 return dg.renderType(w, child_type);
1366 if (!child_type.hasRuntimeBitsIgnoreComptime()) {
1367 return w.writeAll("bool");
13651368 }
13661369
1367 if (child_type.abiSize(target) == 0) {
1368 return w.writeAll("bool");
1370 if (t.optionalReprIsPayload()) {
1371 return dg.renderType(w, child_type);
13691372 }
13701373
13711374 const name = dg.getTypedefName(t) orelse
......@@ -1816,8 +1819,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
18161819 .not => try airNot (f, inst),
18171820
18181821 .optional_payload => try airOptionalPayload(f, inst),
1819 .optional_payload_ptr => try airOptionalPayload(f, inst),
1822 .optional_payload_ptr => try airOptionalPayloadPtr(f, inst),
18201823 .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
1824 .wrap_optional => try airWrapOptional(f, inst),
18211825
18221826 .is_err => try airIsErr(f, inst, false, "!="),
18231827 .is_non_err => try airIsErr(f, inst, false, "=="),
......@@ -1846,7 +1850,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
18461850 .cond_br => try airCondBr(f, inst),
18471851 .br => try airBr(f, inst),
18481852 .switch_br => try airSwitchBr(f, inst),
1849 .wrap_optional => try airWrapOptional(f, inst),
18501853 .struct_field_ptr => try airStructFieldPtr(f, inst),
18511854 .array_to_slice => try airArrayToSlice(f, inst),
18521855 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
......@@ -3145,7 +3148,6 @@ fn airIsNull(
31453148 const un_op = f.air.instructions.items(.data)[inst].un_op;
31463149 const writer = f.object.writer();
31473150 const operand = try f.resolveInst(un_op);
3148 const target = f.object.dg.module.getTarget();
31493151
31503152 const local = try f.allocLocal(Type.initTag(.bool), .Const);
31513153 try writer.writeAll(" = (");
......@@ -3153,18 +3155,18 @@ fn airIsNull(
31533155
31543156 const ty = f.air.typeOf(un_op);
31553157 var opt_buf: Type.Payload.ElemType = undefined;
3156 const payload_type = if (ty.zigTypeTag() == .Pointer)
3158 const payload_ty = if (ty.zigTypeTag() == .Pointer)
31573159 ty.childType().optionalChild(&opt_buf)
31583160 else
31593161 ty.optionalChild(&opt_buf);
31603162
3161 if (ty.isPtrLikeOptional()) {
3163 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3164 try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
3165 } else if (ty.isPtrLikeOptional()) {
31623166 // operand is a regular pointer, test `operand !=/== NULL`
31633167 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
3164 } else if (payload_type.zigTypeTag() == .ErrorSet) {
3168 } else if (payload_ty.zigTypeTag() == .ErrorSet) {
31653169 try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator });
3166 } else if (payload_type.abiSize(target) == 0) {
3167 try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
31683170 } else {
31693171 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
31703172 }
......@@ -3172,18 +3174,46 @@ fn airIsNull(
31723174}
31733175
31743176fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
3175 if (f.liveness.isUnused(inst))
3177 if (f.liveness.isUnused(inst)) return CValue.none;
3178
3179 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3180 const writer = f.object.writer();
3181 const operand = try f.resolveInst(ty_op.operand);
3182 const opt_ty = f.air.typeOf(ty_op.operand);
3183
3184 var buf: Type.Payload.ElemType = undefined;
3185 const payload_ty = opt_ty.optionalChild(&buf);
3186
3187 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
31763188 return CValue.none;
3189 }
3190
3191 if (opt_ty.optionalReprIsPayload()) {
3192 return operand;
3193 }
3194
3195 const inst_ty = f.air.typeOfIndex(inst);
3196 const local = try f.allocLocal(inst_ty, .Const);
3197 try writer.writeAll(" = (");
3198 try f.writeCValue(writer, operand);
3199 try writer.writeAll(").payload;\n");
3200 return local;
3201}
3202
3203fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3204 if (f.liveness.isUnused(inst)) return CValue.none;
31773205
31783206 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
31793207 const writer = f.object.writer();
31803208 const operand = try f.resolveInst(ty_op.operand);
3181 const operand_ty = f.air.typeOf(ty_op.operand);
3209 const ptr_ty = f.air.typeOf(ty_op.operand);
3210 const opt_ty = ptr_ty.childType();
3211 var buf: Type.Payload.ElemType = undefined;
3212 const payload_ty = opt_ty.optionalChild(&buf);
31823213
3183 const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
3184 operand_ty.elemType()
3185 else
3186 operand_ty;
3214 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3215 return operand;
3216 }
31873217
31883218 if (opt_ty.optionalReprIsPayload()) {
31893219 // the operand is just a regular pointer, no need to do anything special.
......@@ -3192,14 +3222,10 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
31923222 }
31933223
31943224 const inst_ty = f.air.typeOfIndex(inst);
3195 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
3196 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
3197
31983225 const local = try f.allocLocal(inst_ty, .Const);
3199 try writer.print(" = {s}(", .{maybe_addrof});
3226 try writer.writeAll(" = &(");
32003227 try f.writeCValue(writer, operand);
3201
3202 try writer.print("){s}payload;\n", .{maybe_deref});
3228 try writer.writeAll(")->payload;\n");
32033229 return local;
32043230}
32053231
src/type.zig+54-10
......@@ -2375,7 +2375,6 @@ pub const Type = extern union {
23752375 // These types have more than one possible value, so the result is the same as
23762376 // asking whether they are comptime-only types.
23772377 .anyframe_T,
2378 .optional,
23792378 .optional_single_mut_pointer,
23802379 .optional_single_const_pointer,
23812380 .single_const_pointer,
......@@ -2397,6 +2396,22 @@ pub const Type = extern union {
23972396 }
23982397 },
23992398
2399 .optional => {
2400 var buf: Payload.ElemType = undefined;
2401 const child_ty = ty.optionalChild(&buf);
2402 if (child_ty.isNoReturn()) {
2403 // Then the optional is comptime-known to be null.
2404 return false;
2405 }
2406 if (ignore_comptime_only) {
2407 return true;
2408 } else if (sema_kit) |sk| {
2409 return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, child_ty));
2410 } else {
2411 return !comptimeOnly(child_ty);
2412 }
2413 },
2414
24002415 .error_union => {
24012416 // This code needs to be kept in sync with the equivalent switch prong
24022417 // in abiSizeAdvanced.
......@@ -2665,13 +2680,22 @@ pub const Type = extern union {
26652680 };
26662681 }
26672682
2668 pub fn isNoReturn(self: Type) bool {
2669 const definitely_correct_result =
2670 self.tag_if_small_enough != .bound_fn and
2671 self.zigTypeTag() == .NoReturn;
2672 const fast_result = self.tag_if_small_enough == Tag.noreturn;
2673 assert(fast_result == definitely_correct_result);
2674 return fast_result;
2683 /// TODO add enums with no fields here
2684 pub fn isNoReturn(ty: Type) bool {
2685 switch (ty.tag()) {
2686 .noreturn => return true,
2687 .error_set => {
2688 const err_set_obj = ty.castTag(.error_set).?.data;
2689 const names = err_set_obj.names.keys();
2690 return names.len == 0;
2691 },
2692 .error_set_merged => {
2693 const name_map = ty.castTag(.error_set_merged).?.data;
2694 const names = name_map.keys();
2695 return names.len == 0;
2696 },
2697 else => return false,
2698 }
26752699 }
26762700
26772701 /// Returns 0 if the pointer is naturally aligned and the element type is 0-bit.
......@@ -2918,7 +2942,13 @@ pub const Type = extern union {
29182942
29192943 switch (child_type.zigTypeTag()) {
29202944 .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
2921 .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat),
2945 .ErrorSet => switch (child_type.errorSetCardinality()) {
2946 // `?error{}` is comptime-known to be null.
2947 .zero => return AbiAlignmentAdvanced{ .scalar = 0 },
2948 .one => return AbiAlignmentAdvanced{ .scalar = 1 },
2949 .many => return abiAlignmentAdvanced(Type.anyerror, target, strat),
2950 },
2951 .NoReturn => return AbiAlignmentAdvanced{ .scalar = 0 },
29222952 else => {},
29232953 }
29242954
......@@ -3365,6 +3395,11 @@ pub const Type = extern union {
33653395 .optional => {
33663396 var buf: Payload.ElemType = undefined;
33673397 const child_type = ty.optionalChild(&buf);
3398
3399 if (child_type.isNoReturn()) {
3400 return AbiSizeAdvanced{ .scalar = 0 };
3401 }
3402
33683403 if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
33693404
33703405 switch (child_type.zigTypeTag()) {
......@@ -4804,7 +4839,6 @@ pub const Type = extern union {
48044839 .const_slice,
48054840 .mut_slice,
48064841 .anyopaque,
4807 .optional,
48084842 .optional_single_mut_pointer,
48094843 .optional_single_const_pointer,
48104844 .enum_literal,
......@@ -4839,6 +4873,16 @@ pub const Type = extern union {
48394873 .bound_fn,
48404874 => return null,
48414875
4876 .optional => {
4877 var buf: Payload.ElemType = undefined;
4878 const child_ty = ty.optionalChild(&buf);
4879 if (child_ty.isNoReturn()) {
4880 return Value.@"null";
4881 } else {
4882 return null;
4883 }
4884 },
4885
48424886 .error_set_single => return Value.initTag(.the_only_possible_value),
48434887 .error_set => {
48444888 const err_set_obj = ty.castTag(.error_set).?.data;
test/behavior/error.zig+33-1
......@@ -121,7 +121,7 @@ test "debug info for optional error set" {
121121 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
122122 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
123123
124 const SomeError = error{Hello};
124 const SomeError = error{ Hello, Hello2 };
125125 var a_local_variable: ?SomeError = null;
126126 _ = a_local_variable;
127127}
......@@ -454,6 +454,38 @@ test "optional error set is the same size as error set" {
454454 comptime try expect(S.returnsOptErrSet() == null);
455455}
456456
457test "optional error set with only one error is the same size as bool" {
458 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
459 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
460 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
461
462 const E = error{only};
463 comptime try expect(@sizeOf(?E) == @sizeOf(bool));
464 comptime try expect(@alignOf(?E) == @alignOf(bool));
465 const S = struct {
466 fn gimmeNull() ?E {
467 return null;
468 }
469 fn gimmeErr() ?E {
470 return error.only;
471 }
472 };
473 try expect(S.gimmeNull() == null);
474 try expect(error.only == S.gimmeErr().?);
475 comptime try expect(S.gimmeNull() == null);
476 comptime try expect(error.only == S.gimmeErr().?);
477}
478
479test "optional empty error set" {
480 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
481
482 const T = ?error{};
483 var t: T = undefined;
484 if (t != null) {
485 @compileError("test failed");
486 }
487}
488
457489test "nested catch" {
458490 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
459491 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO