mirror of
https://github.com/ziglang/zig.git
synced 2025-12-09 15:53:08 +00:00
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).
This commit is contained in:
parent
c847a462ae
commit
c711c788f0
11
src/Sema.zig
11
src/Sema.zig
@ -23316,7 +23316,6 @@ pub fn typeHasOnePossibleValue(
|
|||||||
.const_slice,
|
.const_slice,
|
||||||
.mut_slice,
|
.mut_slice,
|
||||||
.anyopaque,
|
.anyopaque,
|
||||||
.optional,
|
|
||||||
.optional_single_mut_pointer,
|
.optional_single_mut_pointer,
|
||||||
.optional_single_const_pointer,
|
.optional_single_const_pointer,
|
||||||
.enum_literal,
|
.enum_literal,
|
||||||
@ -23351,6 +23350,16 @@ pub fn typeHasOnePossibleValue(
|
|||||||
.bound_fn,
|
.bound_fn,
|
||||||
=> return null,
|
=> return null,
|
||||||
|
|
||||||
|
.optional => {
|
||||||
|
var buf: Type.Payload.ElemType = undefined;
|
||||||
|
const child_ty = ty.optionalChild(&buf);
|
||||||
|
if (child_ty.isNoReturn()) {
|
||||||
|
return Value.@"null";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
.error_set_single => {
|
.error_set_single => {
|
||||||
const name = ty.castTag(.error_set_single).?.data;
|
const name = ty.castTag(.error_set_single).?.data;
|
||||||
return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
|
return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
|
|||||||
const bits = @import("bits.zig");
|
const bits = @import("bits.zig");
|
||||||
const abi = @import("abi.zig");
|
const abi = @import("abi.zig");
|
||||||
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
||||||
const errUnionErrOffset = codegen.errUnionErrOffset;
|
const errUnionErrorOffset = codegen.errUnionErrorOffset;
|
||||||
const RegisterManager = abi.RegisterManager;
|
const RegisterManager = abi.RegisterManager;
|
||||||
const RegisterLock = RegisterManager.RegisterLock;
|
const RegisterLock = RegisterManager.RegisterLock;
|
||||||
const Register = bits.Register;
|
const Register = bits.Register;
|
||||||
@ -3615,7 +3615,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
|
|||||||
return MCValue{ .immediate = 0 }; // always false
|
return MCValue{ .immediate = 0 }; // always false
|
||||||
}
|
}
|
||||||
|
|
||||||
const err_off = errUnionErrOffset(ty, self.target.*);
|
const err_off = errUnionErrorOffset(payload_type, self.target.*);
|
||||||
switch (operand) {
|
switch (operand) {
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
const offset = off - @intCast(u32, err_off);
|
const offset = off - @intCast(u32, err_off);
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput;
|
|||||||
const bits = @import("bits.zig");
|
const bits = @import("bits.zig");
|
||||||
const abi = @import("abi.zig");
|
const abi = @import("abi.zig");
|
||||||
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
||||||
const errUnionErrOffset = codegen.errUnionErrOffset;
|
const errUnionErrorOffset = codegen.errUnionErrorOffset;
|
||||||
const RegisterManager = abi.RegisterManager;
|
const RegisterManager = abi.RegisterManager;
|
||||||
const RegisterLock = RegisterManager.RegisterLock;
|
const RegisterLock = RegisterManager.RegisterLock;
|
||||||
const Register = bits.Register;
|
const Register = bits.Register;
|
||||||
@ -1775,7 +1775,7 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV
|
|||||||
return error_union_mcv;
|
return error_union_mcv;
|
||||||
}
|
}
|
||||||
|
|
||||||
const err_offset = @intCast(u32, errUnionErrOffset(error_union_ty, self.target.*));
|
const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
|
||||||
switch (error_union_mcv) {
|
switch (error_union_mcv) {
|
||||||
.register => return self.fail("TODO errUnionErr for registers", .{}),
|
.register => return self.fail("TODO errUnionErr for registers", .{}),
|
||||||
.stack_argument_offset => |off| {
|
.stack_argument_offset => |off| {
|
||||||
@ -1812,7 +1812,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
|
|||||||
return MCValue.none;
|
return MCValue.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload_offset = @intCast(u32, errUnionPayloadOffset(error_union_ty, self.target.*));
|
const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
|
||||||
switch (error_union_mcv) {
|
switch (error_union_mcv) {
|
||||||
.register => return self.fail("TODO errUnionPayload for registers", .{}),
|
.register => return self.fail("TODO errUnionPayload for registers", .{}),
|
||||||
.stack_argument_offset => |off| {
|
.stack_argument_offset => |off| {
|
||||||
|
|||||||
@ -23,7 +23,7 @@ const Mir = @import("Mir.zig");
|
|||||||
const Emit = @import("Emit.zig");
|
const Emit = @import("Emit.zig");
|
||||||
const abi = @import("abi.zig");
|
const abi = @import("abi.zig");
|
||||||
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
||||||
const errUnionErrOffset = codegen.errUnionErrOffset;
|
const errUnionErrorOffset = codegen.errUnionErrorOffset;
|
||||||
|
|
||||||
/// Wasm Value, created when generating an instruction
|
/// Wasm Value, created when generating an instruction
|
||||||
const WValue = union(enum) {
|
const WValue = union(enum) {
|
||||||
@ -2919,10 +2919,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
|||||||
fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
|
fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
|
||||||
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
const un_op = self.air.instructions.items(.data)[inst].un_op;
|
||||||
const operand = try self.resolveInst(un_op);
|
const operand = try self.resolveInst(un_op);
|
||||||
const err_ty = self.air.typeOf(un_op);
|
const err_union_ty = self.air.typeOf(un_op);
|
||||||
const pl_ty = err_ty.errorUnionPayload();
|
const pl_ty = err_union_ty.errorUnionPayload();
|
||||||
|
|
||||||
if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
.i32_ne => return WValue{ .imm32 = 0 },
|
.i32_ne => return WValue{ .imm32 = 0 },
|
||||||
.i32_eq => return WValue{ .imm32 = 1 },
|
.i32_eq => return WValue{ .imm32 = 1 },
|
||||||
@ -2933,7 +2933,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
|
|||||||
try self.emitWValue(operand);
|
try self.emitWValue(operand);
|
||||||
if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
|
if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||||
try self.addMemArg(.i32_load16_u, .{
|
try self.addMemArg(.i32_load16_u, .{
|
||||||
.offset = operand.offset() + @intCast(u32, errUnionErrOffset(pl_ty, self.target)),
|
.offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)),
|
||||||
.alignment = Type.anyerror.abiAlignment(self.target),
|
.alignment = Type.anyerror.abiAlignment(self.target),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2985,7 +2985,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
|
|||||||
return operand;
|
return operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrOffset(payload_ty, self.target)));
|
return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||||
@ -3011,7 +3011,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
|||||||
// ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
|
// ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
|
||||||
try self.emitWValue(err_union);
|
try self.emitWValue(err_union);
|
||||||
try self.addImm32(0);
|
try self.addImm32(0);
|
||||||
const err_val_offset = @intCast(u32, errUnionErrOffset(pl_ty, self.target));
|
const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
|
||||||
try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
|
try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
|
||||||
|
|
||||||
return err_union;
|
return err_union;
|
||||||
@ -3031,7 +3031,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
|||||||
|
|
||||||
const err_union = try self.allocStack(err_ty);
|
const err_union = try self.allocStack(err_ty);
|
||||||
// store error value
|
// store error value
|
||||||
try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrOffset(pl_ty, self.target)));
|
try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target)));
|
||||||
|
|
||||||
// write 'undefined' to the payload
|
// write 'undefined' to the payload
|
||||||
const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new);
|
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
|
|||||||
operand,
|
operand,
|
||||||
.{ .imm32 = 0 },
|
.{ .imm32 = 0 },
|
||||||
Type.anyerror,
|
Type.anyerror,
|
||||||
@intCast(u32, errUnionErrOffset(payload_ty, self.target)),
|
@intCast(u32, errUnionErrorOffset(payload_ty, self.target)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
|
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const Value = @import("../../value.zig").Value;
|
|||||||
const bits = @import("bits.zig");
|
const bits = @import("bits.zig");
|
||||||
const abi = @import("abi.zig");
|
const abi = @import("abi.zig");
|
||||||
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
|
||||||
const errUnionErrOffset = codegen.errUnionErrOffset;
|
const errUnionErrorOffset = codegen.errUnionErrorOffset;
|
||||||
|
|
||||||
const callee_preserved_regs = abi.callee_preserved_regs;
|
const callee_preserved_regs = abi.callee_preserved_regs;
|
||||||
const caller_preserved_regs = abi.caller_preserved_regs;
|
const caller_preserved_regs = abi.caller_preserved_regs;
|
||||||
@ -1799,7 +1799,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
break :result operand;
|
break :result operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
const err_off = errUnionErrOffset(err_union_ty, self.target.*);
|
const err_off = errUnionErrorOffset(payload_ty, self.target.*);
|
||||||
switch (operand) {
|
switch (operand) {
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
const offset = off - @intCast(i32, err_off);
|
const offset = off - @intCast(i32, err_off);
|
||||||
@ -1844,7 +1844,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
break :result MCValue.none;
|
break :result MCValue.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload_off = errUnionPayloadOffset(err_union_ty, self.target.*);
|
const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
|
||||||
switch (operand) {
|
switch (operand) {
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
const offset = off - @intCast(i32, payload_off);
|
const offset = off - @intCast(i32, payload_off);
|
||||||
@ -1978,8 +1978,8 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
|
const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
|
||||||
const abi_align = error_union_ty.abiAlignment(self.target.*);
|
const abi_align = error_union_ty.abiAlignment(self.target.*);
|
||||||
const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
|
const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
|
||||||
const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*);
|
const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
|
||||||
const err_off = errUnionErrOffset(error_union_ty, self.target.*);
|
const err_off = errUnionErrorOffset(payload_ty, self.target.*);
|
||||||
try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{});
|
try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{});
|
||||||
try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), .{ .immediate = 0 }, .{});
|
try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), .{ .immediate = 0 }, .{});
|
||||||
|
|
||||||
@ -2007,8 +2007,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
|
const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
|
||||||
const abi_align = error_union_ty.abiAlignment(self.target.*);
|
const abi_align = error_union_ty.abiAlignment(self.target.*);
|
||||||
const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
|
const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align));
|
||||||
const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*);
|
const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
|
||||||
const err_off = errUnionErrOffset(error_union_ty, self.target.*);
|
const err_off = errUnionErrorOffset(payload_ty, self.target.*);
|
||||||
try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{});
|
try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{});
|
||||||
try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), .undef, .{});
|
try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), .undef, .{});
|
||||||
|
|
||||||
@ -4670,7 +4670,7 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
|
|||||||
try self.spillCompareFlagsIfOccupied();
|
try self.spillCompareFlagsIfOccupied();
|
||||||
self.compare_flags_inst = inst;
|
self.compare_flags_inst = inst;
|
||||||
|
|
||||||
const err_off = errUnionErrOffset(ty, self.target.*);
|
const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);
|
||||||
switch (operand) {
|
switch (operand) {
|
||||||
.stack_offset => |off| {
|
.stack_offset => |off| {
|
||||||
const offset = off - @intCast(i32, err_off);
|
const offset = off - @intCast(i32, err_off);
|
||||||
|
|||||||
@ -891,18 +891,22 @@ fn lowerDeclRef(
|
|||||||
return Result{ .appended = {} };
|
return Result{ .appended = {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 {
|
pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
|
||||||
const payload_ty = ty.errorUnionPayload();
|
const payload_align = payload_ty.abiAlignment(target);
|
||||||
return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target))
|
const error_align = Type.anyerror.abiAlignment(target);
|
||||||
Type.anyerror.abiSize(target)
|
if (payload_align >= error_align) {
|
||||||
else
|
return 0;
|
||||||
0;
|
} else {
|
||||||
|
return mem.alignForwardGeneric(u64, Type.anyerror.abiSize(target), payload_align);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errUnionErrOffset(ty: Type, target: std.Target) u64 {
|
pub fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u64 {
|
||||||
const payload_ty = ty.errorUnionPayload();
|
const payload_align = payload_ty.abiAlignment(target);
|
||||||
return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target))
|
const error_align = Type.anyerror.abiAlignment(target);
|
||||||
0
|
if (payload_align >= error_align) {
|
||||||
else
|
return mem.alignForwardGeneric(u64, payload_ty.abiSize(target), error_align);
|
||||||
payload_ty.abiSize(target);
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -711,21 +711,24 @@ pub const DeclGen = struct {
|
|||||||
.Bool => return writer.print("{}", .{val.toBool()}),
|
.Bool => return writer.print("{}", .{val.toBool()}),
|
||||||
.Optional => {
|
.Optional => {
|
||||||
var opt_buf: Type.Payload.ElemType = undefined;
|
var opt_buf: Type.Payload.ElemType = undefined;
|
||||||
const payload_type = ty.optionalChild(&opt_buf);
|
const payload_ty = ty.optionalChild(&opt_buf);
|
||||||
if (ty.optionalReprIsPayload()) {
|
|
||||||
return dg.renderValue(writer, payload_type, val, location);
|
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||||
}
|
|
||||||
if (payload_type.abiSize(target) == 0) {
|
|
||||||
const is_null = val.castTag(.opt_payload) == null;
|
const is_null = val.castTag(.opt_payload) == null;
|
||||||
return writer.print("{}", .{is_null});
|
return writer.print("{}", .{is_null});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ty.optionalReprIsPayload()) {
|
||||||
|
return dg.renderValue(writer, payload_ty, val, location);
|
||||||
|
}
|
||||||
|
|
||||||
try writer.writeByte('(');
|
try writer.writeByte('(');
|
||||||
try dg.renderTypecast(writer, ty);
|
try dg.renderTypecast(writer, ty);
|
||||||
try writer.writeAll("){");
|
try writer.writeAll("){");
|
||||||
if (val.castTag(.opt_payload)) |pl| {
|
if (val.castTag(.opt_payload)) |pl| {
|
||||||
const payload_val = pl.data;
|
const payload_val = pl.data;
|
||||||
try writer.writeAll(" .is_null = false, .payload = ");
|
try writer.writeAll(" .is_null = false, .payload = ");
|
||||||
try dg.renderValue(writer, payload_type, payload_val, location);
|
try dg.renderValue(writer, payload_ty, payload_val, location);
|
||||||
try writer.writeAll(" }");
|
try writer.writeAll(" }");
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll(" .is_null = true }");
|
try writer.writeAll(" .is_null = true }");
|
||||||
@ -1360,12 +1363,12 @@ pub const DeclGen = struct {
|
|||||||
var opt_buf: Type.Payload.ElemType = undefined;
|
var opt_buf: Type.Payload.ElemType = undefined;
|
||||||
const child_type = t.optionalChild(&opt_buf);
|
const child_type = t.optionalChild(&opt_buf);
|
||||||
|
|
||||||
if (t.optionalReprIsPayload()) {
|
if (!child_type.hasRuntimeBitsIgnoreComptime()) {
|
||||||
return dg.renderType(w, child_type);
|
return w.writeAll("bool");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (child_type.abiSize(target) == 0) {
|
if (t.optionalReprIsPayload()) {
|
||||||
return w.writeAll("bool");
|
return dg.renderType(w, child_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = dg.getTypedefName(t) orelse
|
const name = dg.getTypedefName(t) orelse
|
||||||
@ -1816,8 +1819,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
|||||||
.not => try airNot (f, inst),
|
.not => try airNot (f, inst),
|
||||||
|
|
||||||
.optional_payload => try airOptionalPayload(f, inst),
|
.optional_payload => try airOptionalPayload(f, inst),
|
||||||
.optional_payload_ptr => try airOptionalPayload(f, inst),
|
.optional_payload_ptr => try airOptionalPayloadPtr(f, inst),
|
||||||
.optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
|
.optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
|
||||||
|
.wrap_optional => try airWrapOptional(f, inst),
|
||||||
|
|
||||||
.is_err => try airIsErr(f, inst, false, "!="),
|
.is_err => try airIsErr(f, inst, false, "!="),
|
||||||
.is_non_err => try airIsErr(f, inst, false, "=="),
|
.is_non_err => try airIsErr(f, inst, false, "=="),
|
||||||
@ -1846,7 +1850,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
|||||||
.cond_br => try airCondBr(f, inst),
|
.cond_br => try airCondBr(f, inst),
|
||||||
.br => try airBr(f, inst),
|
.br => try airBr(f, inst),
|
||||||
.switch_br => try airSwitchBr(f, inst),
|
.switch_br => try airSwitchBr(f, inst),
|
||||||
.wrap_optional => try airWrapOptional(f, inst),
|
|
||||||
.struct_field_ptr => try airStructFieldPtr(f, inst),
|
.struct_field_ptr => try airStructFieldPtr(f, inst),
|
||||||
.array_to_slice => try airArrayToSlice(f, inst),
|
.array_to_slice => try airArrayToSlice(f, inst),
|
||||||
.cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
|
.cmpxchg_weak => try airCmpxchg(f, inst, "weak"),
|
||||||
@ -3145,7 +3148,6 @@ fn airIsNull(
|
|||||||
const un_op = f.air.instructions.items(.data)[inst].un_op;
|
const un_op = f.air.instructions.items(.data)[inst].un_op;
|
||||||
const writer = f.object.writer();
|
const writer = f.object.writer();
|
||||||
const operand = try f.resolveInst(un_op);
|
const operand = try f.resolveInst(un_op);
|
||||||
const target = f.object.dg.module.getTarget();
|
|
||||||
|
|
||||||
const local = try f.allocLocal(Type.initTag(.bool), .Const);
|
const local = try f.allocLocal(Type.initTag(.bool), .Const);
|
||||||
try writer.writeAll(" = (");
|
try writer.writeAll(" = (");
|
||||||
@ -3153,18 +3155,18 @@ fn airIsNull(
|
|||||||
|
|
||||||
const ty = f.air.typeOf(un_op);
|
const ty = f.air.typeOf(un_op);
|
||||||
var opt_buf: Type.Payload.ElemType = undefined;
|
var opt_buf: Type.Payload.ElemType = undefined;
|
||||||
const payload_type = if (ty.zigTypeTag() == .Pointer)
|
const payload_ty = if (ty.zigTypeTag() == .Pointer)
|
||||||
ty.childType().optionalChild(&opt_buf)
|
ty.childType().optionalChild(&opt_buf)
|
||||||
else
|
else
|
||||||
ty.optionalChild(&opt_buf);
|
ty.optionalChild(&opt_buf);
|
||||||
|
|
||||||
if (ty.isPtrLikeOptional()) {
|
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||||
|
try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
|
||||||
|
} else if (ty.isPtrLikeOptional()) {
|
||||||
// operand is a regular pointer, test `operand !=/== NULL`
|
// operand is a regular pointer, test `operand !=/== NULL`
|
||||||
try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
|
try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
|
||||||
} else if (payload_type.zigTypeTag() == .ErrorSet) {
|
} else if (payload_ty.zigTypeTag() == .ErrorSet) {
|
||||||
try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator });
|
try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator });
|
||||||
} else if (payload_type.abiSize(target) == 0) {
|
|
||||||
try writer.print("){s} {s} true;\n", .{ deref_suffix, operator });
|
|
||||||
} else {
|
} else {
|
||||||
try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
|
try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator });
|
||||||
}
|
}
|
||||||
@ -3172,18 +3174,46 @@ fn airIsNull(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
|
fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||||
if (f.liveness.isUnused(inst))
|
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||||
return CValue.none;
|
|
||||||
|
|
||||||
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
|
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
|
||||||
const writer = f.object.writer();
|
const writer = f.object.writer();
|
||||||
const operand = try f.resolveInst(ty_op.operand);
|
const operand = try f.resolveInst(ty_op.operand);
|
||||||
const operand_ty = f.air.typeOf(ty_op.operand);
|
const opt_ty = f.air.typeOf(ty_op.operand);
|
||||||
|
|
||||||
const opt_ty = if (operand_ty.zigTypeTag() == .Pointer)
|
var buf: Type.Payload.ElemType = undefined;
|
||||||
operand_ty.elemType()
|
const payload_ty = opt_ty.optionalChild(&buf);
|
||||||
else
|
|
||||||
operand_ty;
|
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||||
|
return CValue.none;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt_ty.optionalReprIsPayload()) {
|
||||||
|
return operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inst_ty = f.air.typeOfIndex(inst);
|
||||||
|
const local = try f.allocLocal(inst_ty, .Const);
|
||||||
|
try writer.writeAll(" = (");
|
||||||
|
try f.writeCValue(writer, operand);
|
||||||
|
try writer.writeAll(").payload;\n");
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||||
|
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||||
|
|
||||||
|
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
|
||||||
|
const writer = f.object.writer();
|
||||||
|
const operand = try f.resolveInst(ty_op.operand);
|
||||||
|
const ptr_ty = f.air.typeOf(ty_op.operand);
|
||||||
|
const opt_ty = ptr_ty.childType();
|
||||||
|
var buf: Type.Payload.ElemType = undefined;
|
||||||
|
const payload_ty = opt_ty.optionalChild(&buf);
|
||||||
|
|
||||||
|
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||||
|
return operand;
|
||||||
|
}
|
||||||
|
|
||||||
if (opt_ty.optionalReprIsPayload()) {
|
if (opt_ty.optionalReprIsPayload()) {
|
||||||
// the operand is just a regular pointer, no need to do anything special.
|
// 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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const inst_ty = f.air.typeOfIndex(inst);
|
const inst_ty = f.air.typeOfIndex(inst);
|
||||||
const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
|
|
||||||
const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
|
|
||||||
|
|
||||||
const local = try f.allocLocal(inst_ty, .Const);
|
const local = try f.allocLocal(inst_ty, .Const);
|
||||||
try writer.print(" = {s}(", .{maybe_addrof});
|
try writer.writeAll(" = &(");
|
||||||
try f.writeCValue(writer, operand);
|
try f.writeCValue(writer, operand);
|
||||||
|
try writer.writeAll(")->payload;\n");
|
||||||
try writer.print("){s}payload;\n", .{maybe_deref});
|
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
64
src/type.zig
64
src/type.zig
@ -2375,7 +2375,6 @@ pub const Type = extern union {
|
|||||||
// These types have more than one possible value, so the result is the same as
|
// These types have more than one possible value, so the result is the same as
|
||||||
// asking whether they are comptime-only types.
|
// asking whether they are comptime-only types.
|
||||||
.anyframe_T,
|
.anyframe_T,
|
||||||
.optional,
|
|
||||||
.optional_single_mut_pointer,
|
.optional_single_mut_pointer,
|
||||||
.optional_single_const_pointer,
|
.optional_single_const_pointer,
|
||||||
.single_const_pointer,
|
.single_const_pointer,
|
||||||
@ -2397,6 +2396,22 @@ pub const Type = extern union {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.optional => {
|
||||||
|
var buf: Payload.ElemType = undefined;
|
||||||
|
const child_ty = ty.optionalChild(&buf);
|
||||||
|
if (child_ty.isNoReturn()) {
|
||||||
|
// Then the optional is comptime-known to be null.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ignore_comptime_only) {
|
||||||
|
return true;
|
||||||
|
} else if (sema_kit) |sk| {
|
||||||
|
return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, child_ty));
|
||||||
|
} else {
|
||||||
|
return !comptimeOnly(child_ty);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
.error_union => {
|
.error_union => {
|
||||||
// This code needs to be kept in sync with the equivalent switch prong
|
// This code needs to be kept in sync with the equivalent switch prong
|
||||||
// in abiSizeAdvanced.
|
// in abiSizeAdvanced.
|
||||||
@ -2665,13 +2680,22 @@ pub const Type = extern union {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isNoReturn(self: Type) bool {
|
/// TODO add enums with no fields here
|
||||||
const definitely_correct_result =
|
pub fn isNoReturn(ty: Type) bool {
|
||||||
self.tag_if_small_enough != .bound_fn and
|
switch (ty.tag()) {
|
||||||
self.zigTypeTag() == .NoReturn;
|
.noreturn => return true,
|
||||||
const fast_result = self.tag_if_small_enough == Tag.noreturn;
|
.error_set => {
|
||||||
assert(fast_result == definitely_correct_result);
|
const err_set_obj = ty.castTag(.error_set).?.data;
|
||||||
return fast_result;
|
const names = err_set_obj.names.keys();
|
||||||
|
return names.len == 0;
|
||||||
|
},
|
||||||
|
.error_set_merged => {
|
||||||
|
const name_map = ty.castTag(.error_set_merged).?.data;
|
||||||
|
const names = name_map.keys();
|
||||||
|
return names.len == 0;
|
||||||
|
},
|
||||||
|
else => return false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns 0 if the pointer is naturally aligned and the element type is 0-bit.
|
/// Returns 0 if the pointer is naturally aligned and the element type is 0-bit.
|
||||||
@ -2918,7 +2942,13 @@ pub const Type = extern union {
|
|||||||
|
|
||||||
switch (child_type.zigTypeTag()) {
|
switch (child_type.zigTypeTag()) {
|
||||||
.Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
|
.Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
|
||||||
.ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat),
|
.ErrorSet => switch (child_type.errorSetCardinality()) {
|
||||||
|
// `?error{}` is comptime-known to be null.
|
||||||
|
.zero => return AbiAlignmentAdvanced{ .scalar = 0 },
|
||||||
|
.one => return AbiAlignmentAdvanced{ .scalar = 1 },
|
||||||
|
.many => return abiAlignmentAdvanced(Type.anyerror, target, strat),
|
||||||
|
},
|
||||||
|
.NoReturn => return AbiAlignmentAdvanced{ .scalar = 0 },
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3365,6 +3395,11 @@ pub const Type = extern union {
|
|||||||
.optional => {
|
.optional => {
|
||||||
var buf: Payload.ElemType = undefined;
|
var buf: Payload.ElemType = undefined;
|
||||||
const child_type = ty.optionalChild(&buf);
|
const child_type = ty.optionalChild(&buf);
|
||||||
|
|
||||||
|
if (child_type.isNoReturn()) {
|
||||||
|
return AbiSizeAdvanced{ .scalar = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
|
if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
|
||||||
|
|
||||||
switch (child_type.zigTypeTag()) {
|
switch (child_type.zigTypeTag()) {
|
||||||
@ -4804,7 +4839,6 @@ pub const Type = extern union {
|
|||||||
.const_slice,
|
.const_slice,
|
||||||
.mut_slice,
|
.mut_slice,
|
||||||
.anyopaque,
|
.anyopaque,
|
||||||
.optional,
|
|
||||||
.optional_single_mut_pointer,
|
.optional_single_mut_pointer,
|
||||||
.optional_single_const_pointer,
|
.optional_single_const_pointer,
|
||||||
.enum_literal,
|
.enum_literal,
|
||||||
@ -4839,6 +4873,16 @@ pub const Type = extern union {
|
|||||||
.bound_fn,
|
.bound_fn,
|
||||||
=> return null,
|
=> return null,
|
||||||
|
|
||||||
|
.optional => {
|
||||||
|
var buf: Payload.ElemType = undefined;
|
||||||
|
const child_ty = ty.optionalChild(&buf);
|
||||||
|
if (child_ty.isNoReturn()) {
|
||||||
|
return Value.@"null";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
.error_set_single => return Value.initTag(.the_only_possible_value),
|
.error_set_single => return Value.initTag(.the_only_possible_value),
|
||||||
.error_set => {
|
.error_set => {
|
||||||
const err_set_obj = ty.castTag(.error_set).?.data;
|
const err_set_obj = ty.castTag(.error_set).?.data;
|
||||||
|
|||||||
@ -121,7 +121,7 @@ test "debug info for optional error set" {
|
|||||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||||
|
|
||||||
const SomeError = error{Hello};
|
const SomeError = error{ Hello, Hello2 };
|
||||||
var a_local_variable: ?SomeError = null;
|
var a_local_variable: ?SomeError = null;
|
||||||
_ = a_local_variable;
|
_ = a_local_variable;
|
||||||
}
|
}
|
||||||
@ -454,6 +454,38 @@ test "optional error set is the same size as error set" {
|
|||||||
comptime try expect(S.returnsOptErrSet() == null);
|
comptime try expect(S.returnsOptErrSet() == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "optional error set with only one error is the same size as bool" {
|
||||||
|
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||||
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
|
const E = error{only};
|
||||||
|
comptime try expect(@sizeOf(?E) == @sizeOf(bool));
|
||||||
|
comptime try expect(@alignOf(?E) == @alignOf(bool));
|
||||||
|
const S = struct {
|
||||||
|
fn gimmeNull() ?E {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
fn gimmeErr() ?E {
|
||||||
|
return error.only;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try expect(S.gimmeNull() == null);
|
||||||
|
try expect(error.only == S.gimmeErr().?);
|
||||||
|
comptime try expect(S.gimmeNull() == null);
|
||||||
|
comptime try expect(error.only == S.gimmeErr().?);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "optional empty error set" {
|
||||||
|
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||||
|
|
||||||
|
const T = ?error{};
|
||||||
|
var t: T = undefined;
|
||||||
|
if (t != null) {
|
||||||
|
@compileError("test failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "nested catch" {
|
test "nested catch" {
|
||||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user