stage2: make ?anyerror represented the same as anyerror
I was able to get the backend implementation working on LLVM and the C backend, but I'm going to ask for some help on the other backends.
This commit is contained in:
@@ -1386,7 +1386,7 @@ fn isByRef(ty: Type, target: std.Target) bool {
|
||||
return true;
|
||||
},
|
||||
.Optional => {
|
||||
if (ty.isPtrLikeOptional()) return false;
|
||||
if (ty.optionalReprIsPayload()) return false;
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();
|
||||
},
|
||||
@@ -1832,6 +1832,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
|
||||
if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
return self.store(lhs, rhs, Type.u8, 0);
|
||||
}
|
||||
if (pl_ty.zigTypeTag() == .ErrorSet) {
|
||||
return self.store(lhs, rhs, Type.anyerror, 0);
|
||||
}
|
||||
|
||||
const len = @intCast(u32, ty.abiSize(self.target));
|
||||
return self.memcpy(lhs, rhs, .{ .imm32 = len });
|
||||
@@ -2198,7 +2201,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
|
||||
const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty);
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const payload_ty = payload_ptr.container_ty.optionalChild(&buf);
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.optionalReprIsPayload()) {
|
||||
return parent_ptr;
|
||||
}
|
||||
|
||||
@@ -2353,7 +2356,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
|
||||
const err_val = if (!is_pl) val else Value.initTag(.zero);
|
||||
return self.lowerConstant(err_val, error_type);
|
||||
},
|
||||
.Optional => if (ty.isPtrLikeOptional()) {
|
||||
.Optional => if (ty.optionalReprIsPayload()) {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const pl_ty = ty.optionalChild(&buf);
|
||||
if (val.castTag(.opt_payload)) |payload| {
|
||||
@@ -2392,7 +2395,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
|
||||
.Optional => {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const pl_ty = ty.optionalChild(&buf);
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
if (ty.optionalReprIsPayload()) {
|
||||
return self.emitUndefined(pl_ty);
|
||||
}
|
||||
return WValue{ .imm32 = 0xaaaaaaaa };
|
||||
@@ -2542,7 +2545,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
|
||||
}
|
||||
|
||||
fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue {
|
||||
if (ty.zigTypeTag() == .Optional and !ty.isPtrLikeOptional()) {
|
||||
if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const payload_ty = ty.optionalChild(&buf);
|
||||
if (payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
@@ -3120,7 +3123,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en
|
||||
|
||||
fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
|
||||
try self.emitWValue(operand);
|
||||
if (!optional_ty.isPtrLikeOptional()) {
|
||||
if (!optional_ty.optionalReprIsPayload()) {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const payload_ty = optional_ty.optionalChild(&buf);
|
||||
// When payload is zero-bits, we can treat operand as a value, rather than
|
||||
@@ -3146,7 +3149,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const opt_ty = self.air.typeOf(ty_op.operand);
|
||||
const payload_ty = self.air.typeOfIndex(inst);
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
|
||||
if (opt_ty.isPtrLikeOptional()) return operand;
|
||||
if (opt_ty.optionalReprIsPayload()) return operand;
|
||||
|
||||
const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target);
|
||||
|
||||
@@ -3166,7 +3169,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const payload_ty = opt_ty.optionalChild(&buf);
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.isPtrLikeOptional()) {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
@@ -3184,7 +3187,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
|
||||
return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()});
|
||||
}
|
||||
|
||||
if (opt_ty.isPtrLikeOptional()) {
|
||||
if (opt_ty.optionalReprIsPayload()) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
@@ -3215,7 +3218,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const op_ty = self.air.typeOfIndex(inst);
|
||||
if (op_ty.isPtrLikeOptional()) {
|
||||
if (op_ty.optionalReprIsPayload()) {
|
||||
return operand;
|
||||
}
|
||||
const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch {
|
||||
|
||||
@@ -654,7 +654,7 @@ pub fn generateSymbol(
|
||||
return Result{ .appended = {} };
|
||||
}
|
||||
|
||||
if (typed_value.ty.isPtrLikeOptional()) {
|
||||
if (typed_value.ty.optionalReprIsPayload()) {
|
||||
if (typed_value.val.castTag(.opt_payload)) |payload| {
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = payload_type,
|
||||
|
||||
@@ -712,7 +712,7 @@ pub const DeclGen = struct {
|
||||
.Optional => {
|
||||
var opt_buf: Type.Payload.ElemType = undefined;
|
||||
const payload_type = ty.optionalChild(&opt_buf);
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
if (ty.optionalReprIsPayload()) {
|
||||
return dg.renderValue(writer, payload_type, val, location);
|
||||
}
|
||||
if (payload_type.abiSize(target) == 0) {
|
||||
@@ -1360,7 +1360,7 @@ pub const DeclGen = struct {
|
||||
var opt_buf: Type.Payload.ElemType = undefined;
|
||||
const child_type = t.optionalChild(&opt_buf);
|
||||
|
||||
if (t.isPtrLikeOptional()) {
|
||||
if (t.optionalReprIsPayload()) {
|
||||
return dg.renderType(w, child_type);
|
||||
}
|
||||
|
||||
@@ -3161,6 +3161,8 @@ fn airIsNull(
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
// operand is a regular pointer, test `operand !=/== NULL`
|
||||
try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator });
|
||||
} else if (payload_type.zigTypeTag() == .ErrorSet) {
|
||||
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 {
|
||||
@@ -3183,7 +3185,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
else
|
||||
operand_ty;
|
||||
|
||||
if (opt_ty.isPtrLikeOptional()) {
|
||||
if (opt_ty.optionalReprIsPayload()) {
|
||||
// the operand is just a regular pointer, no need to do anything special.
|
||||
// *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C
|
||||
return operand;
|
||||
@@ -3209,7 +3211,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
|
||||
const opt_ty = operand_ty.elemType();
|
||||
|
||||
if (opt_ty.isPtrLikeOptional()) {
|
||||
if (opt_ty.optionalReprIsPayload()) {
|
||||
// The payload and the optional are the same value.
|
||||
// Setting to non-null will be done when the payload is set.
|
||||
return operand;
|
||||
@@ -3419,8 +3421,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const operand = try f.resolveInst(ty_op.operand);
|
||||
|
||||
const inst_ty = f.air.typeOfIndex(inst);
|
||||
if (inst_ty.isPtrLikeOptional()) {
|
||||
// the operand is just a regular pointer, no need to do anything special.
|
||||
if (inst_ty.optionalReprIsPayload()) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
|
||||
@@ -1390,7 +1390,7 @@ pub const Object = struct {
|
||||
gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty);
|
||||
return di_ty;
|
||||
}
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
if (ty.optionalReprIsPayload()) {
|
||||
const ptr_di_ty = try o.lowerDebugType(child_ty, resolve);
|
||||
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
|
||||
try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module });
|
||||
@@ -1472,6 +1472,12 @@ pub const Object = struct {
|
||||
.ErrorUnion => {
|
||||
const err_set_ty = ty.errorUnionSet();
|
||||
const payload_ty = ty.errorUnionPayload();
|
||||
if (err_set_ty.errorSetCardinality() == .zero) {
|
||||
const payload_di_ty = try o.lowerDebugType(payload_ty, .full);
|
||||
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
|
||||
try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module });
|
||||
return payload_di_ty;
|
||||
}
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full);
|
||||
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
|
||||
@@ -2439,7 +2445,7 @@ pub const DeclGen = struct {
|
||||
return dg.context.intType(1);
|
||||
}
|
||||
const payload_llvm_ty = try dg.llvmType(child_ty);
|
||||
if (t.isPtrLikeOptional()) {
|
||||
if (t.optionalReprIsPayload()) {
|
||||
return payload_llvm_ty;
|
||||
}
|
||||
|
||||
@@ -3058,7 +3064,7 @@ pub const DeclGen = struct {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
return non_null_bit;
|
||||
}
|
||||
if (tv.ty.isPtrLikeOptional()) {
|
||||
if (tv.ty.optionalReprIsPayload()) {
|
||||
if (tv.val.castTag(.opt_payload)) |payload| {
|
||||
return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data });
|
||||
} else if (is_pl) {
|
||||
@@ -3557,7 +3563,9 @@ pub const DeclGen = struct {
|
||||
const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf);
|
||||
bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module);
|
||||
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or
|
||||
payload_ty.optionalReprIsPayload())
|
||||
{
|
||||
// In this case, we represent pointer to optional the same as pointer
|
||||
// to the payload.
|
||||
break :blk parent_llvm_ptr;
|
||||
@@ -4461,7 +4469,9 @@ pub const FuncGen = struct {
|
||||
.Int, .Bool, .Pointer, .ErrorSet => scalar_ty,
|
||||
.Optional => blk: {
|
||||
const payload_ty = operand_ty.optionalChild(&opt_buffer);
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.isPtrLikeOptional()) {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or
|
||||
operand_ty.optionalReprIsPayload())
|
||||
{
|
||||
break :blk operand_ty;
|
||||
}
|
||||
// We need to emit instructions to check for equality/inequality
|
||||
@@ -5414,7 +5424,7 @@ pub const FuncGen = struct {
|
||||
const operand = try self.resolveInst(un_op);
|
||||
const operand_ty = self.air.typeOf(un_op);
|
||||
const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
if (optional_ty.optionalReprIsPayload()) {
|
||||
const optional_llvm_ty = try self.dg.llvmType(optional_ty);
|
||||
const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
|
||||
return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
|
||||
@@ -5499,7 +5509,7 @@ pub const FuncGen = struct {
|
||||
const res_ptr_ty = try self.dg.llvmType(result_ty);
|
||||
return self.builder.buildBitCast(operand, res_ptr_ty, "");
|
||||
}
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
if (optional_ty.optionalReprIsPayload()) {
|
||||
// The payload and the optional are the same value.
|
||||
return operand;
|
||||
}
|
||||
@@ -5527,7 +5537,7 @@ pub const FuncGen = struct {
|
||||
const res_ptr_ty = try self.dg.llvmType(result_ty);
|
||||
return self.builder.buildBitCast(operand, res_ptr_ty, "");
|
||||
}
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
if (optional_ty.optionalReprIsPayload()) {
|
||||
// The payload and the optional are the same value.
|
||||
// Setting to non-null will be done when the payload is set.
|
||||
return operand;
|
||||
@@ -5561,7 +5571,7 @@ pub const FuncGen = struct {
|
||||
const payload_ty = self.air.typeOfIndex(inst);
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null;
|
||||
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
if (optional_ty.optionalReprIsPayload()) {
|
||||
// Payload value is the same as the optional value.
|
||||
return operand;
|
||||
}
|
||||
@@ -5702,7 +5712,9 @@ pub const FuncGen = struct {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const optional_ty = self.air.typeOfIndex(inst);
|
||||
if (optional_ty.isPtrLikeOptional()) return operand;
|
||||
if (optional_ty.optionalReprIsPayload()) {
|
||||
return operand;
|
||||
}
|
||||
const llvm_optional_ty = try self.dg.llvmType(optional_ty);
|
||||
if (isByRef(optional_ty)) {
|
||||
const optional_ptr = self.buildAlloca(llvm_optional_ty);
|
||||
@@ -7038,7 +7050,7 @@ pub const FuncGen = struct {
|
||||
}
|
||||
const success_bit = self.builder.buildExtractValue(result, 1, "");
|
||||
|
||||
if (optional_ty.isPtrLikeOptional()) {
|
||||
if (optional_ty.optionalReprIsPayload()) {
|
||||
return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, "");
|
||||
}
|
||||
|
||||
|
||||
58
src/type.zig
58
src/type.zig
@@ -2916,8 +2916,10 @@ pub const Type = extern union {
|
||||
var buf: Payload.ElemType = undefined;
|
||||
const child_type = ty.optionalChild(&buf);
|
||||
|
||||
if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) {
|
||||
return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
|
||||
switch (child_type.zigTypeTag()) {
|
||||
.Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
|
||||
.ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat),
|
||||
else => {},
|
||||
}
|
||||
|
||||
switch (strat) {
|
||||
@@ -3365,14 +3367,29 @@ pub const Type = extern union {
|
||||
const child_type = ty.optionalChild(&buf);
|
||||
if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 };
|
||||
|
||||
if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice())
|
||||
return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
|
||||
switch (child_type.zigTypeTag()) {
|
||||
.Pointer => {
|
||||
const ptr_info = child_type.ptrInfo().data;
|
||||
const has_null = switch (ptr_info.size) {
|
||||
.Slice, .C => true,
|
||||
else => ptr_info.@"allowzero",
|
||||
};
|
||||
if (!has_null) {
|
||||
const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8);
|
||||
return AbiSizeAdvanced{ .scalar = ptr_size_bytes };
|
||||
}
|
||||
},
|
||||
.ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat),
|
||||
else => {},
|
||||
}
|
||||
|
||||
// Optional types are represented as a struct with the child type as the first
|
||||
// field and a boolean as the second. Since the child type's abi alignment is
|
||||
// guaranteed to be >= that of bool's (1 byte) the added size is exactly equal
|
||||
// to the child type's ABI alignment.
|
||||
return AbiSizeAdvanced{ .scalar = child_type.abiAlignment(target) + child_type.abiSize(target) };
|
||||
return AbiSizeAdvanced{
|
||||
.scalar = child_type.abiAlignment(target) + child_type.abiSize(target),
|
||||
};
|
||||
},
|
||||
|
||||
.error_union => {
|
||||
@@ -3901,8 +3918,39 @@ pub const Type = extern union {
|
||||
return ty.ptrInfo().data.@"allowzero";
|
||||
}
|
||||
|
||||
/// See also `isPtrLikeOptional`.
|
||||
pub fn optionalReprIsPayload(ty: Type) bool {
|
||||
switch (ty.tag()) {
|
||||
.optional_single_const_pointer,
|
||||
.optional_single_mut_pointer,
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
=> return true,
|
||||
|
||||
.optional => {
|
||||
const child_ty = ty.castTag(.optional).?.data;
|
||||
switch (child_ty.zigTypeTag()) {
|
||||
.Pointer => {
|
||||
const info = child_ty.ptrInfo().data;
|
||||
switch (info.size) {
|
||||
.Slice, .C => return false,
|
||||
.Many, .One => return !info.@"allowzero",
|
||||
}
|
||||
},
|
||||
.ErrorSet => return true,
|
||||
else => return false,
|
||||
}
|
||||
},
|
||||
|
||||
.pointer => return ty.castTag(.pointer).?.data.size == .C,
|
||||
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the type is optional and would be lowered to a single pointer
|
||||
/// address value, using 0 for null. Note that this returns true for C pointers.
|
||||
/// See also `hasOptionalRepr`.
|
||||
pub fn isPtrLikeOptional(self: Type) bool {
|
||||
switch (self.tag()) {
|
||||
.optional_single_const_pointer,
|
||||
|
||||
@@ -433,9 +433,8 @@ test "return function call to error set from error union function" {
|
||||
}
|
||||
|
||||
test "optional error set is the same size as error set" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror));
|
||||
comptime try expect(@alignOf(?anyerror) == @alignOf(anyerror));
|
||||
const S = struct {
|
||||
fn returnsOptErrSet() ?anyerror {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user