mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 13:30:45 +00:00
stage2: treat error{}!void as a zero-bit type
This commit is contained in:
parent
c711c788f0
commit
1f16b07d6f
24
src/Sema.zig
24
src/Sema.zig
@ -23320,7 +23320,6 @@ pub fn typeHasOnePossibleValue(
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
.anyerror_void_error_union,
|
||||
.error_union,
|
||||
.error_set_inferred,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
@ -23360,6 +23359,29 @@ pub fn typeHasOnePossibleValue(
|
||||
}
|
||||
},
|
||||
|
||||
.error_union => {
|
||||
const error_ty = ty.errorUnionSet();
|
||||
switch (error_ty.errorSetCardinality()) {
|
||||
.zero => {
|
||||
const payload_ty = ty.errorUnionPayload();
|
||||
if (try typeHasOnePossibleValue(sema, block, src, payload_ty)) |payload_val| {
|
||||
return try Value.Tag.eu_payload.create(sema.arena, payload_val);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.one => {
|
||||
if (ty.errorUnionPayload().isNoReturn()) {
|
||||
const error_val = (try typeHasOnePossibleValue(sema, block, src, error_ty)).?;
|
||||
return error_val;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.many => return null,
|
||||
}
|
||||
},
|
||||
|
||||
.error_set_single => {
|
||||
const name = ty.castTag(.error_set_single).?.data;
|
||||
return try Value.Tag.@"error".create(sema.arena, .{ .name = name });
|
||||
|
||||
@ -1470,16 +1470,25 @@ pub const Object = struct {
|
||||
return full_di_ty;
|
||||
},
|
||||
.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;
|
||||
switch (ty.errorUnionSet().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;
|
||||
},
|
||||
.one => {
|
||||
if (payload_ty.isNoReturn()) {
|
||||
const di_type = dib.createBasicType("void", 0, DW.ATE.signed);
|
||||
gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type);
|
||||
return di_type;
|
||||
}
|
||||
},
|
||||
.many => {},
|
||||
}
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full);
|
||||
const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full);
|
||||
// The recursive call to `lowerDebugType` means we can't use `gop` anymore.
|
||||
try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module });
|
||||
return err_set_di_ty;
|
||||
@ -1502,56 +1511,51 @@ pub const Object = struct {
|
||||
break :blk fwd_decl;
|
||||
};
|
||||
|
||||
const err_set_size = err_set_ty.abiSize(target);
|
||||
const err_set_align = err_set_ty.abiAlignment(target);
|
||||
const error_size = Type.anyerror.abiSize(target);
|
||||
const error_align = Type.anyerror.abiAlignment(target);
|
||||
const payload_size = payload_ty.abiSize(target);
|
||||
const payload_align = payload_ty.abiAlignment(target);
|
||||
|
||||
var offset: u64 = 0;
|
||||
offset += err_set_size;
|
||||
offset = std.mem.alignForwardGeneric(u64, offset, payload_align);
|
||||
const payload_offset = offset;
|
||||
|
||||
var len: u8 = 2;
|
||||
var fields: [3]*llvm.DIType = .{
|
||||
dib.createMemberType(
|
||||
fwd_decl.toScope(),
|
||||
"tag",
|
||||
di_file,
|
||||
line,
|
||||
err_set_size * 8, // size in bits
|
||||
err_set_align * 8, // align in bits
|
||||
0, // offset in bits
|
||||
0, // flags
|
||||
try o.lowerDebugType(err_set_ty, .full),
|
||||
),
|
||||
dib.createMemberType(
|
||||
fwd_decl.toScope(),
|
||||
"value",
|
||||
di_file,
|
||||
line,
|
||||
payload_size * 8, // size in bits
|
||||
payload_align * 8, // align in bits
|
||||
payload_offset * 8, // offset in bits
|
||||
0, // flags
|
||||
try o.lowerDebugType(payload_ty, .full),
|
||||
),
|
||||
undefined,
|
||||
};
|
||||
|
||||
const error_size = Type.anyerror.abiSize(target);
|
||||
if (payload_align > error_size) {
|
||||
fields[2] = fields[1];
|
||||
const pad_len = @intCast(u32, payload_align - error_size);
|
||||
fields[1] = dib.createArrayType(
|
||||
pad_len * 8,
|
||||
8,
|
||||
try o.lowerDebugType(Type.u8, .full),
|
||||
@intCast(c_int, pad_len),
|
||||
);
|
||||
len += 1;
|
||||
var error_index: u32 = undefined;
|
||||
var payload_index: u32 = undefined;
|
||||
var error_offset: u64 = undefined;
|
||||
var payload_offset: u64 = undefined;
|
||||
if (error_align > payload_align) {
|
||||
error_index = 0;
|
||||
payload_index = 1;
|
||||
error_offset = 0;
|
||||
payload_offset = std.mem.alignForwardGeneric(u64, error_size, payload_align);
|
||||
} else {
|
||||
payload_index = 0;
|
||||
error_index = 1;
|
||||
payload_offset = 0;
|
||||
error_offset = std.mem.alignForwardGeneric(u64, payload_size, error_align);
|
||||
}
|
||||
|
||||
var fields: [2]*llvm.DIType = undefined;
|
||||
fields[error_index] = dib.createMemberType(
|
||||
fwd_decl.toScope(),
|
||||
"tag",
|
||||
di_file,
|
||||
line,
|
||||
error_size * 8, // size in bits
|
||||
error_align * 8, // align in bits
|
||||
error_offset * 8, // offset in bits
|
||||
0, // flags
|
||||
try o.lowerDebugType(Type.anyerror, .full),
|
||||
);
|
||||
fields[payload_index] = dib.createMemberType(
|
||||
fwd_decl.toScope(),
|
||||
"value",
|
||||
di_file,
|
||||
line,
|
||||
payload_size * 8, // size in bits
|
||||
payload_align * 8, // align in bits
|
||||
payload_offset * 8, // offset in bits
|
||||
0, // flags
|
||||
try o.lowerDebugType(payload_ty, .full),
|
||||
);
|
||||
|
||||
const full_di_ty = dib.createStructType(
|
||||
compile_unit_scope,
|
||||
name.ptr,
|
||||
@ -1562,7 +1566,7 @@ pub const Object = struct {
|
||||
0, // flags
|
||||
null, // derived from
|
||||
&fields,
|
||||
len,
|
||||
fields.len,
|
||||
0, // run time lang
|
||||
null, // vtable holder
|
||||
"", // unique id
|
||||
@ -2455,18 +2459,23 @@ pub const DeclGen = struct {
|
||||
return dg.context.structType(&fields, fields.len, .False);
|
||||
},
|
||||
.ErrorUnion => {
|
||||
const error_type = t.errorUnionSet();
|
||||
const payload_type = t.errorUnionPayload();
|
||||
if (error_type.errorSetCardinality() == .zero) {
|
||||
return dg.lowerType(payload_type);
|
||||
const payload_ty = t.errorUnionPayload();
|
||||
switch (t.errorUnionSet().errorSetCardinality()) {
|
||||
.zero => return dg.lowerType(payload_ty),
|
||||
.one => {
|
||||
if (payload_ty.isNoReturn()) {
|
||||
return dg.context.voidType();
|
||||
}
|
||||
},
|
||||
.many => {},
|
||||
}
|
||||
if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
return try dg.lowerType(Type.anyerror);
|
||||
}
|
||||
const llvm_error_type = try dg.lowerType(error_type);
|
||||
const llvm_payload_type = try dg.lowerType(payload_type);
|
||||
const llvm_error_type = try dg.lowerType(Type.anyerror);
|
||||
const llvm_payload_type = try dg.lowerType(payload_ty);
|
||||
|
||||
const payload_align = payload_type.abiAlignment(target);
|
||||
const payload_align = payload_ty.abiAlignment(target);
|
||||
const error_align = Type.anyerror.abiAlignment(target);
|
||||
if (error_align > payload_align) {
|
||||
const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
|
||||
@ -2476,9 +2485,7 @@ pub const DeclGen = struct {
|
||||
return dg.context.structType(&fields, fields.len, .False);
|
||||
}
|
||||
},
|
||||
.ErrorSet => {
|
||||
return dg.context.intType(16);
|
||||
},
|
||||
.ErrorSet => return dg.context.intType(16),
|
||||
.Struct => {
|
||||
const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
|
||||
if (gop.found_existing) return gop.value_ptr.*;
|
||||
@ -3095,7 +3102,7 @@ pub const DeclGen = struct {
|
||||
return dg.resolveLlvmFunction(fn_decl_index);
|
||||
},
|
||||
.ErrorSet => {
|
||||
const llvm_ty = try dg.lowerType(tv.ty);
|
||||
const llvm_ty = try dg.lowerType(Type.anyerror);
|
||||
switch (tv.val.tag()) {
|
||||
.@"error" => {
|
||||
const err_name = tv.val.castTag(.@"error").?.data.name;
|
||||
@ -3109,9 +3116,8 @@ pub const DeclGen = struct {
|
||||
}
|
||||
},
|
||||
.ErrorUnion => {
|
||||
const error_type = tv.ty.errorUnionSet();
|
||||
const payload_type = tv.ty.errorUnionPayload();
|
||||
if (error_type.errorSetCardinality() == .zero) {
|
||||
if (tv.ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||
const payload_val = tv.val.castTag(.eu_payload).?.data;
|
||||
return dg.lowerValue(.{ .ty = payload_type, .val = payload_val });
|
||||
}
|
||||
@ -3120,13 +3126,13 @@ pub const DeclGen = struct {
|
||||
if (!payload_type.hasRuntimeBitsIgnoreComptime()) {
|
||||
// We use the error type directly as the type.
|
||||
const err_val = if (!is_pl) tv.val else Value.initTag(.zero);
|
||||
return dg.lowerValue(.{ .ty = error_type, .val = err_val });
|
||||
return dg.lowerValue(.{ .ty = Type.anyerror, .val = err_val });
|
||||
}
|
||||
|
||||
const payload_align = payload_type.abiAlignment(target);
|
||||
const error_align = Type.anyerror.abiAlignment(target);
|
||||
const llvm_error_value = try dg.lowerValue(.{
|
||||
.ty = error_type,
|
||||
.ty = Type.anyerror,
|
||||
.val = if (is_pl) Value.initTag(.zero) else tv.val,
|
||||
});
|
||||
const llvm_payload_value = try dg.lowerValue(.{
|
||||
@ -5656,13 +5662,12 @@ pub const FuncGen = struct {
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const error_union_ty = self.air.typeOf(ty_op.operand).childType();
|
||||
|
||||
const error_ty = error_union_ty.errorUnionSet();
|
||||
if (error_ty.errorSetCardinality() == .zero) {
|
||||
if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||
// TODO: write undefined bytes through the pointer here
|
||||
return operand;
|
||||
}
|
||||
const payload_ty = error_union_ty.errorUnionPayload();
|
||||
const non_error_val = try self.dg.lowerValue(.{ .ty = error_ty, .val = Value.zero });
|
||||
const non_error_val = try self.dg.lowerValue(.{ .ty = Type.anyerror, .val = Value.zero });
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
_ = self.builder.buildStore(non_error_val, operand);
|
||||
return operand;
|
||||
@ -6715,9 +6720,9 @@ pub const FuncGen = struct {
|
||||
if (self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const operand_ty = self.air.typeOf(ty_op.operand);
|
||||
const inst_ty = self.air.typeOfIndex(inst);
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const operand_is_ref = isByRef(operand_ty);
|
||||
const result_is_ref = isByRef(inst_ty);
|
||||
const llvm_dest_ty = try self.dg.lowerType(inst_ty);
|
||||
|
||||
65
src/type.zig
65
src/type.zig
@ -2416,14 +2416,18 @@ pub const Type = extern union {
|
||||
// This code needs to be kept in sync with the equivalent switch prong
|
||||
// in abiSizeAdvanced.
|
||||
const data = ty.castTag(.error_union).?.data;
|
||||
if (data.error_set.errorSetCardinality() == .zero) {
|
||||
return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit);
|
||||
} else if (ignore_comptime_only) {
|
||||
return true;
|
||||
} else if (sema_kit) |sk| {
|
||||
return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
|
||||
} else {
|
||||
return !comptimeOnly(ty);
|
||||
switch (data.error_set.errorSetCardinality()) {
|
||||
.zero => return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit),
|
||||
.one => return !data.payload.isNoReturn(),
|
||||
.many => {
|
||||
if (ignore_comptime_only) {
|
||||
return true;
|
||||
} else if (sema_kit) |sk| {
|
||||
return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
|
||||
} else {
|
||||
return !comptimeOnly(ty);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@ -2970,8 +2974,14 @@ pub const Type = extern union {
|
||||
// This code needs to be kept in sync with the equivalent switch prong
|
||||
// in abiSizeAdvanced.
|
||||
const data = ty.castTag(.error_union).?.data;
|
||||
if (data.error_set.errorSetCardinality() == .zero) {
|
||||
return abiAlignmentAdvanced(data.payload, target, strat);
|
||||
switch (data.error_set.errorSetCardinality()) {
|
||||
.zero => return abiAlignmentAdvanced(data.payload, target, strat),
|
||||
.one => {
|
||||
if (data.payload.isNoReturn()) {
|
||||
return AbiAlignmentAdvanced{ .scalar = 0 };
|
||||
}
|
||||
},
|
||||
.many => {},
|
||||
}
|
||||
const code_align = abiAlignment(Type.anyerror, target);
|
||||
switch (strat) {
|
||||
@ -3440,8 +3450,14 @@ pub const Type = extern union {
|
||||
// 1 bit of data which is whether or not the value is an error.
|
||||
// Zig still uses the error code encoding at runtime, even when only 1 bit
|
||||
// would suffice. This prevents coercions from needing to branch.
|
||||
if (data.error_set.errorSetCardinality() == .zero) {
|
||||
return abiSizeAdvanced(data.payload, target, strat);
|
||||
switch (data.error_set.errorSetCardinality()) {
|
||||
.zero => return abiSizeAdvanced(data.payload, target, strat),
|
||||
.one => {
|
||||
if (data.payload.isNoReturn()) {
|
||||
return AbiSizeAdvanced{ .scalar = 0 };
|
||||
}
|
||||
},
|
||||
.many => {},
|
||||
}
|
||||
const code_size = abiSize(Type.anyerror, target);
|
||||
if (!data.payload.hasRuntimeBits()) {
|
||||
@ -4843,7 +4859,6 @@ pub const Type = extern union {
|
||||
.optional_single_const_pointer,
|
||||
.enum_literal,
|
||||
.anyerror_void_error_union,
|
||||
.error_union,
|
||||
.error_set_inferred,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
@ -4883,6 +4898,30 @@ pub const Type = extern union {
|
||||
}
|
||||
},
|
||||
|
||||
.error_union => {
|
||||
const error_ty = ty.errorUnionSet();
|
||||
switch (error_ty.errorSetCardinality()) {
|
||||
.zero => {
|
||||
const payload_ty = ty.errorUnionPayload();
|
||||
if (onePossibleValue(payload_ty)) |payload_val| {
|
||||
_ = payload_val;
|
||||
return Value.initTag(.the_only_possible_value);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.one => {
|
||||
if (ty.errorUnionPayload().isNoReturn()) {
|
||||
const error_val = onePossibleValue(error_ty).?;
|
||||
return error_val;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.many => return null,
|
||||
}
|
||||
},
|
||||
|
||||
.error_set_single => return Value.initTag(.the_only_possible_value),
|
||||
.error_set => {
|
||||
const err_set_obj = ty.castTag(.error_set).?.data;
|
||||
|
||||
@ -479,13 +479,40 @@ test "optional error set with only one error is the same size as bool" {
|
||||
test "optional empty error set" {
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
|
||||
const T = ?error{};
|
||||
var t: T = undefined;
|
||||
if (t != null) {
|
||||
comptime try expect(@sizeOf(error{}!void) == @sizeOf(void));
|
||||
comptime try expect(@alignOf(error{}!void) == @alignOf(void));
|
||||
|
||||
var x: ?error{} = undefined;
|
||||
if (x != null) {
|
||||
@compileError("test failed");
|
||||
}
|
||||
}
|
||||
|
||||
test "empty error set plus zero-bit payload" {
|
||||
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
|
||||
|
||||
comptime try expect(@sizeOf(error{}!void) == @sizeOf(void));
|
||||
comptime try expect(@alignOf(error{}!void) == @alignOf(void));
|
||||
|
||||
var x: error{}!void = undefined;
|
||||
if (x) |payload| {
|
||||
if (payload != {}) {
|
||||
@compileError("test failed");
|
||||
}
|
||||
} else |_| {
|
||||
@compileError("test failed");
|
||||
}
|
||||
const S = struct {
|
||||
fn empty() error{}!void {}
|
||||
fn inferred() !void {
|
||||
return empty();
|
||||
}
|
||||
};
|
||||
try S.inferred();
|
||||
}
|
||||
|
||||
test "nested catch" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user