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(...@@ -23316,7 +23316,6 @@ pub fn typeHasOnePossibleValue(
23316 .const_slice,23316 .const_slice,
23317 .mut_slice,23317 .mut_slice,
23318 .anyopaque,23318 .anyopaque,
23319 .optional,
23320 .optional_single_mut_pointer,23319 .optional_single_mut_pointer,
23321 .optional_single_const_pointer,23320 .optional_single_const_pointer,
23322 .enum_literal,23321 .enum_literal,
...@@ -23351,6 +23350,16 @@ pub fn typeHasOnePossibleValue(...@@ -23351,6 +23350,16 @@ pub fn typeHasOnePossibleValue(
23351 .bound_fn,23350 .bound_fn,
23352 => return null,23351 => 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
23354 .error_set_single => {23363 .error_set_single => {
23355 const name = ty.castTag(.error_set_single).?.data;23364 const name = ty.castTag(.error_set_single).?.data;
23356 return try Value.Tag.@"error".create(sema.arena, .{ .name = name });23365 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;...@@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
30const bits = @import("bits.zig");30const bits = @import("bits.zig");
31const abi = @import("abi.zig");31const abi = @import("abi.zig");
32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrOffset = codegen.errUnionErrOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
34const RegisterManager = abi.RegisterManager;34const RegisterManager = abi.RegisterManager;
35const RegisterLock = RegisterManager.RegisterLock;35const RegisterLock = RegisterManager.RegisterLock;
36const Register = bits.Register;36const Register = bits.Register;
...@@ -3615,7 +3615,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {...@@ -3615,7 +3615,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
3615 return MCValue{ .immediate = 0 }; // always false3615 return MCValue{ .immediate = 0 }; // always false
3616 }3616 }
36173617
3618 const err_off = errUnionErrOffset(ty, self.target.*);3618 const err_off = errUnionErrorOffset(payload_type, self.target.*);
3619 switch (operand) {3619 switch (operand) {
3620 .stack_offset => |off| {3620 .stack_offset => |off| {
3621 const offset = off - @intCast(u32, err_off);3621 const offset = off - @intCast(u32, err_off);
src/arch/arm/CodeGen.zig+3-3
...@@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;...@@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
30const bits = @import("bits.zig");30const bits = @import("bits.zig");
31const abi = @import("abi.zig");31const abi = @import("abi.zig");
32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrOffset = codegen.errUnionErrOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
34const RegisterManager = abi.RegisterManager;34const RegisterManager = abi.RegisterManager;
35const RegisterLock = RegisterManager.RegisterLock;35const RegisterLock = RegisterManager.RegisterLock;
36const Register = bits.Register;36const Register = bits.Register;
...@@ -1775,7 +1775,7 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV...@@ -1775,7 +1775,7 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV
1775 return error_union_mcv;1775 return error_union_mcv;
1776 }1776 }
17771777
1778 const err_offset = @intCast(u32, errUnionErrOffset(error_union_ty, self.target.*));1778 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
1779 switch (error_union_mcv) {1779 switch (error_union_mcv) {
1780 .register => return self.fail("TODO errUnionErr for registers", .{}),1780 .register => return self.fail("TODO errUnionErr for registers", .{}),
1781 .stack_argument_offset => |off| {1781 .stack_argument_offset => |off| {
...@@ -1812,7 +1812,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)...@@ -1812,7 +1812,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
1812 return MCValue.none;1812 return MCValue.none;
1813 }1813 }
18141814
1815 const payload_offset = @intCast(u32, errUnionPayloadOffset(error_union_ty, self.target.*));1815 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
1816 switch (error_union_mcv) {1816 switch (error_union_mcv) {
1817 .register => return self.fail("TODO errUnionPayload for registers", .{}),1817 .register => return self.fail("TODO errUnionPayload for registers", .{}),
1818 .stack_argument_offset => |off| {1818 .stack_argument_offset => |off| {
src/arch/wasm/CodeGen.zig+9-9
...@@ -23,7 +23,7 @@ const Mir = @import("Mir.zig");...@@ -23,7 +23,7 @@ const Mir = @import("Mir.zig");
23const Emit = @import("Emit.zig");23const Emit = @import("Emit.zig");
24const abi = @import("abi.zig");24const abi = @import("abi.zig");
25const errUnionPayloadOffset = codegen.errUnionPayloadOffset;25const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
26const errUnionErrOffset = codegen.errUnionErrOffset;26const errUnionErrorOffset = codegen.errUnionErrorOffset;
2727
28/// Wasm Value, created when generating an instruction28/// Wasm Value, created when generating an instruction
29const WValue = union(enum) {29const WValue = union(enum) {
...@@ -2919,10 +2919,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2919,10 +2919,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2919fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {2919fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
2920 const un_op = self.air.instructions.items(.data)[inst].un_op;2920 const un_op = self.air.instructions.items(.data)[inst].un_op;
2921 const operand = try self.resolveInst(un_op);2921 const operand = try self.resolveInst(un_op);
2922 const err_ty = self.air.typeOf(un_op);2922 const err_union_ty = self.air.typeOf(un_op);
2923 const pl_ty = err_ty.errorUnionPayload();2923 const pl_ty = err_union_ty.errorUnionPayload();
29242924
2925 if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {2925 if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
2926 switch (opcode) {2926 switch (opcode) {
2927 .i32_ne => return WValue{ .imm32 = 0 },2927 .i32_ne => return WValue{ .imm32 = 0 },
2928 .i32_eq => return WValue{ .imm32 = 1 },2928 .i32_eq => return WValue{ .imm32 = 1 },
...@@ -2933,7 +2933,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W...@@ -2933,7 +2933,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
2933 try self.emitWValue(operand);2933 try self.emitWValue(operand);
2934 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {2934 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
2935 try self.addMemArg(.i32_load16_u, .{2935 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)),
2937 .alignment = Type.anyerror.abiAlignment(self.target),2937 .alignment = Type.anyerror.abiAlignment(self.target),
2938 });2938 });
2939 }2939 }
...@@ -2985,7 +2985,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In...@@ -2985,7 +2985,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
2985 return operand;2985 return operand;
2986 }2986 }
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)));
2989}2989}
29902990
2991fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2991fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -3011,7 +3011,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3011,7 +3011,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3011 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.3011 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
3012 try self.emitWValue(err_union);3012 try self.emitWValue(err_union);
3013 try self.addImm32(0);3013 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));
3015 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });3015 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
30163016
3017 return err_union;3017 return err_union;
...@@ -3031,7 +3031,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3031,7 +3031,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30313031
3032 const err_union = try self.allocStack(err_ty);3032 const err_union = try self.allocStack(err_ty);
3033 // store error value3033 // 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
3036 // write 'undefined' to the payload3036 // write 'undefined' to the payload
3037 const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);3037 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...@@ -3986,7 +3986,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
3986 operand,3986 operand,
3987 .{ .imm32 = 0 },3987 .{ .imm32 = 0 },
3988 Type.anyerror,3988 Type.anyerror,
3989 @intCast(u32, errUnionErrOffset(payload_ty, self.target)),3989 @intCast(u32, errUnionErrorOffset(payload_ty, self.target)),
3990 );3990 );
39913991
3992 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3992 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;...@@ -30,7 +30,7 @@ const Value = @import("../../value.zig").Value;
30const bits = @import("bits.zig");30const bits = @import("bits.zig");
31const abi = @import("abi.zig");31const abi = @import("abi.zig");
32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;32const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrOffset = codegen.errUnionErrOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
35const callee_preserved_regs = abi.callee_preserved_regs;35const callee_preserved_regs = abi.callee_preserved_regs;
36const caller_preserved_regs = abi.caller_preserved_regs;36const caller_preserved_regs = abi.caller_preserved_regs;
...@@ -1799,7 +1799,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1799,7 +1799,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
1799 break :result operand;1799 break :result operand;
1800 }1800 }
18011801
1802 const err_off = errUnionErrOffset(err_union_ty, self.target.*);1802 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
1803 switch (operand) {1803 switch (operand) {
1804 .stack_offset => |off| {1804 .stack_offset => |off| {
1805 const offset = off - @intCast(i32, err_off);1805 const offset = off - @intCast(i32, err_off);
...@@ -1844,7 +1844,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1844,7 +1844,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1844 break :result MCValue.none;1844 break :result MCValue.none;
1845 }1845 }
18461846
1847 const payload_off = errUnionPayloadOffset(err_union_ty, self.target.*);1847 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
1848 switch (operand) {1848 switch (operand) {
1849 .stack_offset => |off| {1849 .stack_offset => |off| {
1850 const offset = off - @intCast(i32, payload_off);1850 const offset = off - @intCast(i32, payload_off);
...@@ -1978,8 +1978,8 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1978,8 +1978,8 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1978 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));1978 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
1979 const abi_align = error_union_ty.abiAlignment(self.target.*);1979 const abi_align = error_union_ty.abiAlignment(self.target.*);
1980 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));1980 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
1981 const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*);1981 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
1982 const err_off = errUnionErrOffset(error_union_ty, self.target.*);1982 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
1983 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{});1983 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{});
1984 try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), .{ .immediate = 0 }, .{});1984 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 {...@@ -2007,8 +2007,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
2007 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));2007 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
2008 const abi_align = error_union_ty.abiAlignment(self.target.*);2008 const abi_align = error_union_ty.abiAlignment(self.target.*);
2009 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));2009 const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
2010 const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*);2010 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
2011 const err_off = errUnionErrOffset(error_union_ty, self.target.*);2011 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
2012 try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{});2012 try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{});
2013 try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), .undef, .{});2013 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...@@ -4670,7 +4670,7 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
4670 try self.spillCompareFlagsIfOccupied();4670 try self.spillCompareFlagsIfOccupied();
4671 self.compare_flags_inst = inst;4671 self.compare_flags_inst = inst;
46724672
4673 const err_off = errUnionErrOffset(ty, self.target.*);4673 const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);
4674 switch (operand) {4674 switch (operand) {
4675 .stack_offset => |off| {4675 .stack_offset => |off| {
4676 const offset = off - @intCast(i32, err_off);4676 const offset = off - @intCast(i32, err_off);
src/codegen.zig+16-12
...@@ -891,18 +891,22 @@ fn lowerDeclRef(...@@ -891,18 +891,22 @@ fn lowerDeclRef(
891 return Result{ .appended = {} };891 return Result{ .appended = {} };
892}892}
893893
894pub fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 {894pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
895 const payload_ty = ty.errorUnionPayload();895 const payload_align = payload_ty.abiAlignment(target);
896 return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target))896 const error_align = Type.anyerror.abiAlignment(target);
897 Type.anyerror.abiSize(target)897 if (payload_align >= error_align) {
898 else898 return 0;
899 0;899 } else {
900 return mem.alignForwardGeneric(u64, Type.anyerror.abiSize(target), payload_align);
901 }
900}902}
901903
902pub fn errUnionErrOffset(ty: Type, target: std.Target) u64 {904pub fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u64 {
903 const payload_ty = ty.errorUnionPayload();905 const payload_align = payload_ty.abiAlignment(target);
904 return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target))906 const error_align = Type.anyerror.abiAlignment(target);
905 0907 if (payload_align >= error_align) {
906 else908 return mem.alignForwardGeneric(u64, payload_ty.abiSize(target), error_align);
907 payload_ty.abiSize(target);909 } else {
910 return 0;
911 }
908}912}
src/codegen/c.zig+56-30
...@@ -711,21 +711,24 @@ pub const DeclGen = struct {...@@ -711,21 +711,24 @@ pub const DeclGen = struct {
711 .Bool => return writer.print("{}", .{val.toBool()}),711 .Bool => return writer.print("{}", .{val.toBool()}),
712 .Optional => {712 .Optional => {
713 var opt_buf: Type.Payload.ElemType = undefined;713 var opt_buf: Type.Payload.ElemType = undefined;
714 const payload_type = ty.optionalChild(&opt_buf);714 const payload_ty = ty.optionalChild(&opt_buf);
715 if (ty.optionalReprIsPayload()) {715
716 return dg.renderValue(writer, payload_type, val, location);716 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
717 }
718 if (payload_type.abiSize(target) == 0) {
719 const is_null = val.castTag(.opt_payload) == null;717 const is_null = val.castTag(.opt_payload) == null;
720 return writer.print("{}", .{is_null});718 return writer.print("{}", .{is_null});
721 }719 }
720
721 if (ty.optionalReprIsPayload()) {
722 return dg.renderValue(writer, payload_ty, val, location);
723 }
724
722 try writer.writeByte('(');725 try writer.writeByte('(');
723 try dg.renderTypecast(writer, ty);726 try dg.renderTypecast(writer, ty);
724 try writer.writeAll("){");727 try writer.writeAll("){");
725 if (val.castTag(.opt_payload)) |pl| {728 if (val.castTag(.opt_payload)) |pl| {
726 const payload_val = pl.data;729 const payload_val = pl.data;
727 try writer.writeAll(" .is_null = false, .payload = ");730 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);
729 try writer.writeAll(" }");732 try writer.writeAll(" }");
730 } else {733 } else {
731 try writer.writeAll(" .is_null = true }");734 try writer.writeAll(" .is_null = true }");
...@@ -1360,12 +1363,12 @@ pub const DeclGen = struct {...@@ -1360,12 +1363,12 @@ pub const DeclGen = struct {
1360 var opt_buf: Type.Payload.ElemType = undefined;1363 var opt_buf: Type.Payload.ElemType = undefined;
1361 const child_type = t.optionalChild(&opt_buf);1364 const child_type = t.optionalChild(&opt_buf);
13621365
1363 if (t.optionalReprIsPayload()) {1366 if (!child_type.hasRuntimeBitsIgnoreComptime()) {
1364 return dg.renderType(w, child_type);1367 return w.writeAll("bool");
1365 }1368 }
13661369
1367 if (child_type.abiSize(target) == 0) {1370 if (t.optionalReprIsPayload()) {
1368 return w.writeAll("bool");1371 return dg.renderType(w, child_type);
1369 }1372 }
13701373
1371 const name = dg.getTypedefName(t) orelse1374 const name = dg.getTypedefName(t) orelse
...@@ -1816,8 +1819,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1816,8 +1819,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1816 .not => try airNot (f, inst),1819 .not => try airNot (f, inst),
18171820
1818 .optional_payload => try airOptionalPayload(f, inst),1821 .optional_payload => try airOptionalPayload(f, inst),
1819 .optional_payload_ptr => try airOptionalPayload(f, inst),1822 .optional_payload_ptr => try airOptionalPayloadPtr(f, inst),
1820 .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),1823 .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
1824 .wrap_optional => try airWrapOptional(f, inst),
18211825
1822 .is_err => try airIsErr(f, inst, false, "!="),1826 .is_err => try airIsErr(f, inst, false, "!="),
1823 .is_non_err => try airIsErr(f, inst, false, "=="),1827 .is_non_err => try airIsErr(f, inst, false, "=="),
...@@ -1846,7 +1850,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1846,7 +1850,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1846 .cond_br => try airCondBr(f, inst),1850 .cond_br => try airCondBr(f, inst),
1847 .br => try airBr(f, inst),1851 .br => try airBr(f, inst),
1848 .switch_br => try airSwitchBr(f, inst),1852 .switch_br => try airSwitchBr(f, inst),
1849 .wrap_optional => try airWrapOptional(f, inst),
1850 .struct_field_ptr => try airStructFieldPtr(f, inst),1853 .struct_field_ptr => try airStructFieldPtr(f, inst),
1851 .array_to_slice => try airArrayToSlice(f, inst),1854 .array_to_slice => try airArrayToSlice(f, inst),
1852 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),1855 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
...@@ -3145,7 +3148,6 @@ fn airIsNull(...@@ -3145,7 +3148,6 @@ fn airIsNull(
3145 const un_op = f.air.instructions.items(.data)[inst].un_op;3148 const un_op = f.air.instructions.items(.data)[inst].un_op;
3146 const writer = f.object.writer();3149 const writer = f.object.writer();
3147 const operand = try f.resolveInst(un_op);3150 const operand = try f.resolveInst(un_op);
3148 const target = f.object.dg.module.getTarget();
31493151
3150 const local = try f.allocLocal(Type.initTag(.bool), .Const);3152 const local = try f.allocLocal(Type.initTag(.bool), .Const);
3151 try writer.writeAll(" = (");3153 try writer.writeAll(" = (");
...@@ -3153,18 +3155,18 @@ fn airIsNull(...@@ -3153,18 +3155,18 @@ fn airIsNull(
31533155
3154 const ty = f.air.typeOf(un_op);3156 const ty = f.air.typeOf(un_op);
3155 var opt_buf: Type.Payload.ElemType = undefined;3157 var opt_buf: Type.Payload.ElemType = undefined;
3156 const payload_type = if (ty.zigTypeTag() == .Pointer)3158 const payload_ty = if (ty.zigTypeTag() == .Pointer)
3157 ty.childType().optionalChild(&opt_buf)3159 ty.childType().optionalChild(&opt_buf)
3158 else3160 else
3159 ty.optionalChild(&opt_buf);3161 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()) {
3162 // operand is a regular pointer, test `operand !=/== NULL`3166 // operand is a regular pointer, test `operand !=/== NULL`
3163 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });3167 try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
3164 } else if (payload_type.zigTypeTag() == .ErrorSet) {3168 } else if (payload_ty.zigTypeTag() == .ErrorSet) {
3165 try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator });3169 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 });
3168 } else {3170 } else {
3169 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });3171 try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
3170 }3172 }
...@@ -3172,18 +3174,46 @@ fn airIsNull(...@@ -3172,18 +3174,46 @@ fn airIsNull(
3172}3174}
31733175
3174fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {3176fn 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()) {
3176 return CValue.none;3188 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
3178 const ty_op = f.air.instructions.items(.data)[inst].ty_op;3206 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3179 const writer = f.object.writer();3207 const writer = f.object.writer();
3180 const operand = try f.resolveInst(ty_op.operand);3208 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)3214 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3184 operand_ty.elemType()3215 return operand;
3185 else3216 }
3186 operand_ty;
31873217
3188 if (opt_ty.optionalReprIsPayload()) {3218 if (opt_ty.optionalReprIsPayload()) {
3189 // the operand is just a regular pointer, no need to do anything special.3219 // 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 {...@@ -3192,14 +3222,10 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
3192 }3222 }
31933223
3194 const inst_ty = f.air.typeOfIndex(inst);3224 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
3198 const local = try f.allocLocal(inst_ty, .Const);3225 const local = try f.allocLocal(inst_ty, .Const);
3199 try writer.print(" = {s}(", .{maybe_addrof});3226 try writer.writeAll(" = &(");
3200 try f.writeCValue(writer, operand);3227 try f.writeCValue(writer, operand);
32013228 try writer.writeAll(")->payload;\n");
3202 try writer.print("){s}payload;\n", .{maybe_deref});
3203 return local;3229 return local;
3204}3230}
32053231
src/type.zig+54-10
...@@ -2375,7 +2375,6 @@ pub const Type = extern union {...@@ -2375,7 +2375,6 @@ pub const Type = extern union {
2375 // 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
2376 // asking whether they are comptime-only types.2376 // asking whether they are comptime-only types.
2377 .anyframe_T,2377 .anyframe_T,
2378 .optional,
2379 .optional_single_mut_pointer,2378 .optional_single_mut_pointer,
2380 .optional_single_const_pointer,2379 .optional_single_const_pointer,
2381 .single_const_pointer,2380 .single_const_pointer,
...@@ -2397,6 +2396,22 @@ pub const Type = extern union {...@@ -2397,6 +2396,22 @@ pub const Type = extern union {
2397 }2396 }
2398 },2397 },
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
2400 .error_union => {2415 .error_union => {
2401 // This code needs to be kept in sync with the equivalent switch prong2416 // This code needs to be kept in sync with the equivalent switch prong
2402 // in abiSizeAdvanced.2417 // in abiSizeAdvanced.
...@@ -2665,13 +2680,22 @@ pub const Type = extern union {...@@ -2665,13 +2680,22 @@ pub const Type = extern union {
2665 };2680 };
2666 }2681 }
26672682
2668 pub fn isNoReturn(self: Type) bool {2683 /// TODO add enums with no fields here
2669 const definitely_correct_result =2684 pub fn isNoReturn(ty: Type) bool {
2670 self.tag_if_small_enough != .bound_fn and2685 switch (ty.tag()) {
2671 self.zigTypeTag() == .NoReturn;2686 .noreturn => return true,
2672 const fast_result = self.tag_if_small_enough == Tag.noreturn;2687 .error_set => {
2673 assert(fast_result == definitely_correct_result);2688 const err_set_obj = ty.castTag(.error_set).?.data;
2674 return fast_result;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 }
2675 }2699 }
26762700
2677 /// Returns 0 if the pointer is naturally aligned and the element type is 0-bit.2701 /// Returns 0 if the pointer is naturally aligned and the element type is 0-bit.
...@@ -2918,7 +2942,13 @@ pub const Type = extern union {...@@ -2918,7 +2942,13 @@ pub const Type = extern union {
29182942
2919 switch (child_type.zigTypeTag()) {2943 switch (child_type.zigTypeTag()) {
2920 .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },2944 .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 },
2922 else => {},2952 else => {},
2923 }2953 }
29242954
...@@ -3365,6 +3395,11 @@ pub const Type = extern union {...@@ -3365,6 +3395,11 @@ pub const Type = extern union {
3365 .optional => {3395 .optional => {
3366 var buf: Payload.ElemType = undefined;3396 var buf: Payload.ElemType = undefined;
3367 const child_type = ty.optionalChild(&buf);3397 const child_type = ty.optionalChild(&buf);
3398
3399 if (child_type.isNoReturn()) {
3400 return AbiSizeAdvanced{ .scalar = 0 };
3401 }
3402
3368 if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };3403 if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
33693404
3370 switch (child_type.zigTypeTag()) {3405 switch (child_type.zigTypeTag()) {
...@@ -4804,7 +4839,6 @@ pub const Type = extern union {...@@ -4804,7 +4839,6 @@ pub const Type = extern union {
4804 .const_slice,4839 .const_slice,
4805 .mut_slice,4840 .mut_slice,
4806 .anyopaque,4841 .anyopaque,
4807 .optional,
4808 .optional_single_mut_pointer,4842 .optional_single_mut_pointer,
4809 .optional_single_const_pointer,4843 .optional_single_const_pointer,
4810 .enum_literal,4844 .enum_literal,
...@@ -4839,6 +4873,16 @@ pub const Type = extern union {...@@ -4839,6 +4873,16 @@ pub const Type = extern union {
4839 .bound_fn,4873 .bound_fn,
4840 => return null,4874 => 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
4842 .error_set_single => return Value.initTag(.the_only_possible_value),4886 .error_set_single => return Value.initTag(.the_only_possible_value),
4843 .error_set => {4887 .error_set => {
4844 const err_set_obj = ty.castTag(.error_set).?.data;4888 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" {...@@ -121,7 +121,7 @@ test "debug info for optional error set" {
121 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;121 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
122 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;122 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
123123
124 const SomeError = error{Hello};124 const SomeError = error{ Hello, Hello2 };
125 var a_local_variable: ?SomeError = null;125 var a_local_variable: ?SomeError = null;
126 _ = a_local_variable;126 _ = a_local_variable;
127}127}
...@@ -454,6 +454,38 @@ test "optional error set is the same size as error set" {...@@ -454,6 +454,38 @@ test "optional error set is the same size as error set" {
454 comptime try expect(S.returnsOptErrSet() == null);454 comptime try expect(S.returnsOptErrSet() == null);
455}455}
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
457test "nested catch" {489test "nested catch" {
458 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO490 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
459 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO491 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO