Skip to content

Commit

Permalink
InternPool: add representation for value of empty enums and unions
Browse files Browse the repository at this point in the history
This is a bit odd, because this value doesn't actually exist:
see ziglang#15909. This gets all the empty enum/union behavior tests passing.

Also adds an assertion to `Sema.analyzeBodyInner` which would have
helped figure out the issue here much more quickly.
  • Loading branch information
mlugg committed May 31, 2023
1 parent cf68e7b commit 695f24b
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 13 deletions.
24 changes: 23 additions & 1 deletion src/InternPool.zig
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ pub const Key = union(enum) {
enum_literal: NullTerminatedString,
/// A specific enum tag, indicated by the integer tag value.
enum_tag: Key.EnumTag,
/// An empty enum or union. TODO: this value's existence is strange, because such a type in
/// reality has no values. See #15909.
/// Payload is the type for which we are an empty value.
empty_enum_value: Index,
float: Key.Float,
ptr: Ptr,
opt: Opt,
Expand Down Expand Up @@ -662,6 +666,7 @@ pub const Key = union(enum) {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.inferred_error_set_type,
=> |info| std.hash.autoHash(hasher, info),

Expand Down Expand Up @@ -899,6 +904,10 @@ pub const Key = union(enum) {
const b_info = b.enum_tag;
return std.meta.eql(a_info, b_info);
},
.empty_enum_value => |a_info| {
const b_info = b.empty_enum_value;
return a_info == b_info;
},

.variable => |a_info| {
const b_info = b.variable;
Expand Down Expand Up @@ -1134,6 +1143,7 @@ pub const Key = union(enum) {
.enum_literal => .enum_literal_type,

.undef => |x| x,
.empty_enum_value => |x| x,

.simple_value => |s| switch (s) {
.undefined => .undefined_type,
Expand Down Expand Up @@ -1911,6 +1921,7 @@ pub const Tag = enum(u8) {
/// The set of values that are encoded this way is:
/// * An array or vector which has length 0.
/// * A struct which has all fields comptime-known.
/// * An empty enum or union. TODO: this value's existence is strange, because such a type in reality has no values. See #15909
/// data is Index of the type, which is known to be zero bits at runtime.
only_possible_value,
/// data is extra index to Key.Union.
Expand Down Expand Up @@ -2934,6 +2945,13 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
} };
},

.type_enum_auto,
.type_enum_explicit,
.type_union_tagged,
.type_union_untagged,
.type_union_safety,
=> .{ .empty_enum_value = ty },

else => unreachable,
};
},
Expand Down Expand Up @@ -3752,6 +3770,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
});
},

.empty_enum_value => |enum_or_union_ty| ip.items.appendAssumeCapacity(.{
.tag = .only_possible_value,
.data = @enumToInt(enum_or_union_ty),
}),

.float => |float| {
switch (float.ty) {
.f16_type => ip.items.appendAssumeCapacity(.{
Expand Down Expand Up @@ -5412,7 +5435,6 @@ pub fn isNoReturn(ip: InternPool, ty: Index) bool {
.noreturn_type => true,
else => switch (ip.indexToKey(ty)) {
.error_set_type => |error_set_type| error_set_type.names.len == 0,
.enum_type => |enum_type| enum_type.names.len == 0,
else => false,
},
};
Expand Down
29 changes: 19 additions & 10 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1725,8 +1725,12 @@ fn analyzeBodyInner(
break :blk Air.Inst.Ref.void_value;
},
};
if (sema.isNoReturn(air_inst))
if (sema.isNoReturn(air_inst)) {
// We're going to assume that the body itself is noreturn, so let's ensure that now
assert(block.instructions.items.len > 0);
assert(sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])));
break always_noreturn;
}
map.putAssumeCapacity(inst, air_inst);
i += 1;
};
Expand Down Expand Up @@ -31989,6 +31993,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -32854,10 +32859,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
}

if (fields_len == 0) {
return;
}

const bits_per_field = 4;
const fields_per_u32 = 32 / bits_per_field;
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
Expand Down Expand Up @@ -33140,7 +33141,7 @@ fn generateUnionTagTypeNumbered(
.decl = new_decl_index,
.namespace = .none,
.tag_ty = if (enum_field_vals.len == 0)
.noreturn_type
(try mod.intType(.unsigned, 0)).toIntern()
else
mod.intern_pool.typeOf(enum_field_vals[0]),
.names = enum_field_names,
Expand Down Expand Up @@ -33190,7 +33191,7 @@ fn generateUnionTagTypeSimple(
.decl = new_decl_index,
.namespace = .none,
.tag_ty = if (enum_field_names.len == 0)
.noreturn_type
(try mod.intType(.unsigned, 0)).toIntern()
else
(try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(),
.names = enum_field_names,
Expand Down Expand Up @@ -33429,7 +33430,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse
return null;
const fields = union_obj.fields.values();
if (fields.len == 0) return Value.@"unreachable";
if (fields.len == 0) {
const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
return only.toValue();
}
const only_field = fields[0];
if (only_field.ty.eql(resolved_ty, sema.mod)) {
const msg = try Module.ErrorMsg.create(
Expand Down Expand Up @@ -33469,7 +33473,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;

switch (enum_type.names.len) {
0 => return Value.@"unreachable",
0 => {
const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
return only.toValue();
},
1 => return try mod.getCoerced((if (enum_type.values.len == 0)
try mod.intern(.{ .int = .{
.ty = enum_type.tag_ty,
Expand All @@ -33494,6 +33501,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -33982,6 +33990,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -34687,7 +34696,7 @@ fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {

/// Avoids crashing the compiler when asking if inferred allocations are noreturn.
fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
if (ref == .noreturn_type) return true;
if (ref == .unreachable_value) return true;
if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {
.inferred_alloc, .inferred_alloc_comptime => return false,
else => {},
Expand Down
1 change: 1 addition & 0 deletions src/TypedValue.zig
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub fn print(
try writer.writeAll(")");
return;
},
.empty_enum_value => return writer.writeAll("(empty enum value)"),
.float => |float| switch (float.storage) {
inline else => |x| return writer.print("{}", .{x}),
},
Expand Down
1 change: 1 addition & 0 deletions src/arch/wasm/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3053,6 +3053,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
.extern_func,
.func,
.enum_literal,
.empty_enum_value,
=> unreachable, // non-runtime values
.int => {
const int_info = ty.intInfo(mod);
Expand Down
1 change: 1 addition & 0 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ pub fn generateSymbol(
.extern_func,
.func,
.enum_literal,
.empty_enum_value,
=> unreachable, // non-runtime values
.int => {
const abi_size = math.cast(usize, typed_value.ty.abiSize(mod)) orelse return error.Overflow;
Expand Down
1 change: 1 addition & 0 deletions src/codegen/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ pub const DeclGen = struct {
.extern_func,
.func,
.enum_literal,
.empty_enum_value,
=> unreachable, // non-runtime values
.int => |int| switch (int.storage) {
.u64, .i64, .big_int => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}),
Expand Down
1 change: 1 addition & 0 deletions src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3244,6 +3244,7 @@ pub const DeclGen = struct {
},
.variable,
.enum_literal,
.empty_enum_value,
=> unreachable, // non-runtime values
.extern_func, .func => {
const fn_decl_index = switch (val_key) {
Expand Down
1 change: 1 addition & 0 deletions src/codegen/spirv.zig
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ pub const DeclGen = struct {
.extern_func,
.func,
.enum_literal,
.empty_enum_value,
=> unreachable, // non-runtime values
.int => try self.addInt(ty, val),
.err => |err| {
Expand Down
19 changes: 17 additions & 2 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -655,6 +656,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -764,6 +766,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -1098,6 +1101,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -1515,6 +1519,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -1749,6 +1754,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -2305,6 +2311,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -2587,7 +2594,10 @@ pub const Type = struct {
.union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index);
const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null;
if (union_obj.fields.count() == 0) return Value.@"unreachable";
if (union_obj.fields.count() == 0) {
const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
return only.toValue();
}
const only_field = union_obj.fields.values()[0];
const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null;
const only = try mod.intern(.{ .un = .{
Expand Down Expand Up @@ -2616,7 +2626,10 @@ pub const Type = struct {
if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;

switch (enum_type.names.len) {
0 => return Value.@"unreachable",
0 => {
const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
return only.toValue();
},
1 => {
if (enum_type.values.len == 0) {
const only = try mod.intern(.{ .enum_tag = .{
Expand Down Expand Up @@ -2648,6 +2661,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down Expand Up @@ -2793,6 +2807,7 @@ pub const Type = struct {
.error_union,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
.ptr,
.opt,
Expand Down
1 change: 1 addition & 0 deletions src/value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ pub const Value = struct {
.err,
.enum_literal,
.enum_tag,
.empty_enum_value,
.float,
=> val,

Expand Down

0 comments on commit 695f24b

Please sign in to comment.