authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-05-31 04:42:18+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:47:57-07:00
loga0d4ef0acf50db06fdde8ff229d20d15afc7d402
tree92b320040e5a979d569a5284e71c6fcdf7ee893f
parent99531b0d52392668fe9f86b5109fff74cd37aff3

InternPool: add representation for value of empty enums and unions

This is a bit odd, because this value doesn't actually exist: see #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.

10 files changed, 66 insertions(+), 13 deletions(-)

src/InternPool.zig+23-1
...@@ -206,6 +206,10 @@ pub const Key = union(enum) {...@@ -206,6 +206,10 @@ pub const Key = union(enum) {
206 enum_literal: NullTerminatedString,206 enum_literal: NullTerminatedString,
207 /// A specific enum tag, indicated by the integer tag value.207 /// A specific enum tag, indicated by the integer tag value.
208 enum_tag: Key.EnumTag,208 enum_tag: Key.EnumTag,
209 /// An empty enum or union. TODO: this value's existence is strange, because such a type in
210 /// reality has no values. See #15909.
211 /// Payload is the type for which we are an empty value.
212 empty_enum_value: Index,
209 float: Key.Float,213 float: Key.Float,
210 ptr: Ptr,214 ptr: Ptr,
211 opt: Opt,215 opt: Opt,
...@@ -670,6 +674,7 @@ pub const Key = union(enum) {...@@ -670,6 +674,7 @@ pub const Key = union(enum) {
670 .error_union,674 .error_union,
671 .enum_literal,675 .enum_literal,
672 .enum_tag,676 .enum_tag,
677 .empty_enum_value,
673 .inferred_error_set_type,678 .inferred_error_set_type,
674 => |info| {679 => |info| {
675 var hasher = std.hash.Wyhash.init(seed);680 var hasher = std.hash.Wyhash.init(seed);
...@@ -957,6 +962,10 @@ pub const Key = union(enum) {...@@ -957,6 +962,10 @@ pub const Key = union(enum) {
957 const b_info = b.enum_tag;962 const b_info = b.enum_tag;
958 return std.meta.eql(a_info, b_info);963 return std.meta.eql(a_info, b_info);
959 },964 },
965 .empty_enum_value => |a_info| {
966 const b_info = b.empty_enum_value;
967 return a_info == b_info;
968 },
960969
961 .variable => |a_info| {970 .variable => |a_info| {
962 const b_info = b.variable;971 const b_info = b.variable;
...@@ -1192,6 +1201,7 @@ pub const Key = union(enum) {...@@ -1192,6 +1201,7 @@ pub const Key = union(enum) {
1192 .enum_literal => .enum_literal_type,1201 .enum_literal => .enum_literal_type,
11931202
1194 .undef => |x| x,1203 .undef => |x| x,
1204 .empty_enum_value => |x| x,
11951205
1196 .simple_value => |s| switch (s) {1206 .simple_value => |s| switch (s) {
1197 .undefined => .undefined_type,1207 .undefined => .undefined_type,
...@@ -1980,6 +1990,7 @@ pub const Tag = enum(u8) {...@@ -1980,6 +1990,7 @@ pub const Tag = enum(u8) {
1980 /// The set of values that are encoded this way is:1990 /// The set of values that are encoded this way is:
1981 /// * An array or vector which has length 0.1991 /// * An array or vector which has length 0.
1982 /// * A struct which has all fields comptime-known.1992 /// * A struct which has all fields comptime-known.
1993 /// * An empty enum or union. TODO: this value's existence is strange, because such a type in reality has no values. See #15909
1983 /// data is Index of the type, which is known to be zero bits at runtime.1994 /// data is Index of the type, which is known to be zero bits at runtime.
1984 only_possible_value,1995 only_possible_value,
1985 /// data is extra index to Key.Union.1996 /// data is extra index to Key.Union.
...@@ -2952,6 +2963,13 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {...@@ -2952,6 +2963,13 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
2952 } };2963 } };
2953 },2964 },
29542965
2966 .type_enum_auto,
2967 .type_enum_explicit,
2968 .type_union_tagged,
2969 .type_union_untagged,
2970 .type_union_safety,
2971 => .{ .empty_enum_value = ty },
2972
2955 else => unreachable,2973 else => unreachable,
2956 };2974 };
2957 },2975 },
...@@ -3755,6 +3773,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {...@@ -3755,6 +3773,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
3755 });3773 });
3756 },3774 },
37573775
3776 .empty_enum_value => |enum_or_union_ty| ip.items.appendAssumeCapacity(.{
3777 .tag = .only_possible_value,
3778 .data = @enumToInt(enum_or_union_ty),
3779 }),
3780
3758 .float => |float| {3781 .float => |float| {
3759 switch (float.ty) {3782 switch (float.ty) {
3760 .f16_type => ip.items.appendAssumeCapacity(.{3783 .f16_type => ip.items.appendAssumeCapacity(.{
...@@ -5416,7 +5439,6 @@ pub fn isNoReturn(ip: *const InternPool, ty: Index) bool {...@@ -5416,7 +5439,6 @@ pub fn isNoReturn(ip: *const InternPool, ty: Index) bool {
5416 .noreturn_type => true,5439 .noreturn_type => true,
5417 else => switch (ip.indexToKey(ty)) {5440 else => switch (ip.indexToKey(ty)) {
5418 .error_set_type => |error_set_type| error_set_type.names.len == 0,5441 .error_set_type => |error_set_type| error_set_type.names.len == 0,
5419 .enum_type => |enum_type| enum_type.names.len == 0,
5420 else => false,5442 else => false,
5421 },5443 },
5422 };5444 };
src/Sema.zig+19-10
...@@ -1725,8 +1725,12 @@ fn analyzeBodyInner(...@@ -1725,8 +1725,12 @@ fn analyzeBodyInner(
1725 break :blk Air.Inst.Ref.void_value;1725 break :blk Air.Inst.Ref.void_value;
1726 },1726 },
1727 };1727 };
1728 if (sema.isNoReturn(air_inst))1728 if (sema.isNoReturn(air_inst)) {
1729 // We're going to assume that the body itself is noreturn, so let's ensure that now
1730 assert(block.instructions.items.len > 0);
1731 assert(sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])));
1729 break always_noreturn;1732 break always_noreturn;
1733 }
1730 map.putAssumeCapacity(inst, air_inst);1734 map.putAssumeCapacity(inst, air_inst);
1731 i += 1;1735 i += 1;
1732 };1736 };
...@@ -32150,6 +32154,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {...@@ -32150,6 +32154,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
32150 .error_union,32154 .error_union,
32151 .enum_literal,32155 .enum_literal,
32152 .enum_tag,32156 .enum_tag,
32157 .empty_enum_value,
32153 .float,32158 .float,
32154 .ptr,32159 .ptr,
32155 .opt,32160 .opt,
...@@ -33015,10 +33020,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {...@@ -33015,10 +33020,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
33015 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);33020 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
33016 }33021 }
3301733022
33018 if (fields_len == 0) {
33019 return;
33020 }
33021
33022 const bits_per_field = 4;33023 const bits_per_field = 4;
33023 const fields_per_u32 = 32 / bits_per_field;33024 const fields_per_u32 = 32 / bits_per_field;
33024 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;33025 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
...@@ -33301,7 +33302,7 @@ fn generateUnionTagTypeNumbered(...@@ -33301,7 +33302,7 @@ fn generateUnionTagTypeNumbered(
33301 .decl = new_decl_index,33302 .decl = new_decl_index,
33302 .namespace = .none,33303 .namespace = .none,
33303 .tag_ty = if (enum_field_vals.len == 0)33304 .tag_ty = if (enum_field_vals.len == 0)
33304 .noreturn_type33305 (try mod.intType(.unsigned, 0)).toIntern()
33305 else33306 else
33306 mod.intern_pool.typeOf(enum_field_vals[0]),33307 mod.intern_pool.typeOf(enum_field_vals[0]),
33307 .names = enum_field_names,33308 .names = enum_field_names,
...@@ -33351,7 +33352,7 @@ fn generateUnionTagTypeSimple(...@@ -33351,7 +33352,7 @@ fn generateUnionTagTypeSimple(
33351 .decl = new_decl_index,33352 .decl = new_decl_index,
33352 .namespace = .none,33353 .namespace = .none,
33353 .tag_ty = if (enum_field_names.len == 0)33354 .tag_ty = if (enum_field_names.len == 0)
33354 .noreturn_type33355 (try mod.intType(.unsigned, 0)).toIntern()
33355 else33356 else
33356 (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(),33357 (try mod.smallestUnsignedInt(enum_field_names.len - 1)).toIntern(),
33357 .names = enum_field_names,33358 .names = enum_field_names,
...@@ -33590,7 +33591,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -33590,7 +33591,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
33590 const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse33591 const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse
33591 return null;33592 return null;
33592 const fields = union_obj.fields.values();33593 const fields = union_obj.fields.values();
33593 if (fields.len == 0) return Value.@"unreachable";33594 if (fields.len == 0) {
33595 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
33596 return only.toValue();
33597 }
33594 const only_field = fields[0];33598 const only_field = fields[0];
33595 if (only_field.ty.eql(resolved_ty, sema.mod)) {33599 if (only_field.ty.eql(resolved_ty, sema.mod)) {
33596 const msg = try Module.ErrorMsg.create(33600 const msg = try Module.ErrorMsg.create(
...@@ -33630,7 +33634,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -33630,7 +33634,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
33630 if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;33634 if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;
3363133635
33632 switch (enum_type.names.len) {33636 switch (enum_type.names.len) {
33633 0 => return Value.@"unreachable",33637 0 => {
33638 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
33639 return only.toValue();
33640 },
33634 1 => return try mod.getCoerced((if (enum_type.values.len == 0)33641 1 => return try mod.getCoerced((if (enum_type.values.len == 0)
33635 try mod.intern(.{ .int = .{33642 try mod.intern(.{ .int = .{
33636 .ty = enum_type.tag_ty,33643 .ty = enum_type.tag_ty,
...@@ -33655,6 +33662,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {...@@ -33655,6 +33662,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
33655 .error_union,33662 .error_union,
33656 .enum_literal,33663 .enum_literal,
33657 .enum_tag,33664 .enum_tag,
33665 .empty_enum_value,
33658 .float,33666 .float,
33659 .ptr,33667 .ptr,
33660 .opt,33668 .opt,
...@@ -34143,6 +34151,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {...@@ -34143,6 +34151,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
34143 .error_union,34151 .error_union,
34144 .enum_literal,34152 .enum_literal,
34145 .enum_tag,34153 .enum_tag,
34154 .empty_enum_value,
34146 .float,34155 .float,
34147 .ptr,34156 .ptr,
34148 .opt,34157 .opt,
...@@ -34848,7 +34857,7 @@ fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {...@@ -34848,7 +34857,7 @@ fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
3484834857
34849/// Avoids crashing the compiler when asking if inferred allocations are noreturn.34858/// Avoids crashing the compiler when asking if inferred allocations are noreturn.
34850fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {34859fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
34851 if (ref == .noreturn_type) return true;34860 if (ref == .unreachable_value) return true;
34852 if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {34861 if (Air.refToIndex(ref)) |inst| switch (sema.air_instructions.items(.tag)[inst]) {
34853 .inferred_alloc, .inferred_alloc_comptime => return false,34862 .inferred_alloc, .inferred_alloc_comptime => return false,
34854 else => {},34863 else => {},
src/TypedValue.zig+1
...@@ -248,6 +248,7 @@ pub fn print(...@@ -248,6 +248,7 @@ pub fn print(
248 try writer.writeAll(")");248 try writer.writeAll(")");
249 return;249 return;
250 },250 },
251 .empty_enum_value => return writer.writeAll("(empty enum value)"),
251 .float => |float| switch (float.storage) {252 .float => |float| switch (float.storage) {
252 inline else => |x| return writer.print("{}", .{x}),253 inline else => |x| return writer.print("{}", .{x}),
253 },254 },
src/arch/wasm/CodeGen.zig+1
...@@ -3156,6 +3156,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {...@@ -3156,6 +3156,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
3156 .extern_func,3156 .extern_func,
3157 .func,3157 .func,
3158 .enum_literal,3158 .enum_literal,
3159 .empty_enum_value,
3159 => unreachable, // non-runtime values3160 => unreachable, // non-runtime values
3160 .int => {3161 .int => {
3161 const int_info = ty.intInfo(mod);3162 const int_info = ty.intInfo(mod);
src/codegen.zig+1
...@@ -242,6 +242,7 @@ pub fn generateSymbol(...@@ -242,6 +242,7 @@ pub fn generateSymbol(
242 .extern_func,242 .extern_func,
243 .func,243 .func,
244 .enum_literal,244 .enum_literal,
245 .empty_enum_value,
245 => unreachable, // non-runtime values246 => unreachable, // non-runtime values
246 .int => {247 .int => {
247 const abi_size = math.cast(usize, typed_value.ty.abiSize(mod)) orelse return error.Overflow;248 const abi_size = math.cast(usize, typed_value.ty.abiSize(mod)) orelse return error.Overflow;
src/codegen/c.zig+1
...@@ -946,6 +946,7 @@ pub const DeclGen = struct {...@@ -946,6 +946,7 @@ pub const DeclGen = struct {
946 .extern_func,946 .extern_func,
947 .func,947 .func,
948 .enum_literal,948 .enum_literal,
949 .empty_enum_value,
949 => unreachable, // non-runtime values950 => unreachable, // non-runtime values
950 .int => |int| switch (int.storage) {951 .int => |int| switch (int.storage) {
951 .u64, .i64, .big_int => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}),952 .u64, .i64, .big_int => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}),
src/codegen/llvm.zig+1
...@@ -3246,6 +3246,7 @@ pub const DeclGen = struct {...@@ -3246,6 +3246,7 @@ pub const DeclGen = struct {
3246 },3246 },
3247 .variable,3247 .variable,
3248 .enum_literal,3248 .enum_literal,
3249 .empty_enum_value,
3249 => unreachable, // non-runtime values3250 => unreachable, // non-runtime values
3250 .extern_func, .func => {3251 .extern_func, .func => {
3251 const fn_decl_index = switch (val_key) {3252 const fn_decl_index = switch (val_key) {
src/codegen/spirv.zig+1
...@@ -660,6 +660,7 @@ pub const DeclGen = struct {...@@ -660,6 +660,7 @@ pub const DeclGen = struct {
660 .extern_func,660 .extern_func,
661 .func,661 .func,
662 .enum_literal,662 .enum_literal,
663 .empty_enum_value,
663 => unreachable, // non-runtime values664 => unreachable, // non-runtime values
664 .int => try self.addInt(ty, val),665 .int => try self.addInt(ty, val),
665 .err => |err| {666 .err => |err| {
src/type.zig+17-2
...@@ -439,6 +439,7 @@ pub const Type = struct {...@@ -439,6 +439,7 @@ pub const Type = struct {
439 .error_union,439 .error_union,
440 .enum_literal,440 .enum_literal,
441 .enum_tag,441 .enum_tag,
442 .empty_enum_value,
442 .float,443 .float,
443 .ptr,444 .ptr,
444 .opt,445 .opt,
...@@ -655,6 +656,7 @@ pub const Type = struct {...@@ -655,6 +656,7 @@ pub const Type = struct {
655 .error_union,656 .error_union,
656 .enum_literal,657 .enum_literal,
657 .enum_tag,658 .enum_tag,
659 .empty_enum_value,
658 .float,660 .float,
659 .ptr,661 .ptr,
660 .opt,662 .opt,
...@@ -764,6 +766,7 @@ pub const Type = struct {...@@ -764,6 +766,7 @@ pub const Type = struct {
764 .error_union,766 .error_union,
765 .enum_literal,767 .enum_literal,
766 .enum_tag,768 .enum_tag,
769 .empty_enum_value,
767 .float,770 .float,
768 .ptr,771 .ptr,
769 .opt,772 .opt,
...@@ -1098,6 +1101,7 @@ pub const Type = struct {...@@ -1098,6 +1101,7 @@ pub const Type = struct {
1098 .error_union,1101 .error_union,
1099 .enum_literal,1102 .enum_literal,
1100 .enum_tag,1103 .enum_tag,
1104 .empty_enum_value,
1101 .float,1105 .float,
1102 .ptr,1106 .ptr,
1103 .opt,1107 .opt,
...@@ -1515,6 +1519,7 @@ pub const Type = struct {...@@ -1515,6 +1519,7 @@ pub const Type = struct {
1515 .error_union,1519 .error_union,
1516 .enum_literal,1520 .enum_literal,
1517 .enum_tag,1521 .enum_tag,
1522 .empty_enum_value,
1518 .float,1523 .float,
1519 .ptr,1524 .ptr,
1520 .opt,1525 .opt,
...@@ -1749,6 +1754,7 @@ pub const Type = struct {...@@ -1749,6 +1754,7 @@ pub const Type = struct {
1749 .error_union,1754 .error_union,
1750 .enum_literal,1755 .enum_literal,
1751 .enum_tag,1756 .enum_tag,
1757 .empty_enum_value,
1752 .float,1758 .float,
1753 .ptr,1759 .ptr,
1754 .opt,1760 .opt,
...@@ -2302,6 +2308,7 @@ pub const Type = struct {...@@ -2302,6 +2308,7 @@ pub const Type = struct {
2302 .error_union,2308 .error_union,
2303 .enum_literal,2309 .enum_literal,
2304 .enum_tag,2310 .enum_tag,
2311 .empty_enum_value,
2305 .float,2312 .float,
2306 .ptr,2313 .ptr,
2307 .opt,2314 .opt,
...@@ -2584,7 +2591,10 @@ pub const Type = struct {...@@ -2584,7 +2591,10 @@ pub const Type = struct {
2584 .union_type => |union_type| {2591 .union_type => |union_type| {
2585 const union_obj = mod.unionPtr(union_type.index);2592 const union_obj = mod.unionPtr(union_type.index);
2586 const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null;2593 const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null;
2587 if (union_obj.fields.count() == 0) return Value.@"unreachable";2594 if (union_obj.fields.count() == 0) {
2595 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
2596 return only.toValue();
2597 }
2588 const only_field = union_obj.fields.values()[0];2598 const only_field = union_obj.fields.values()[0];
2589 const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null;2599 const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null;
2590 const only = try mod.intern(.{ .un = .{2600 const only = try mod.intern(.{ .un = .{
...@@ -2613,7 +2623,10 @@ pub const Type = struct {...@@ -2613,7 +2623,10 @@ pub const Type = struct {
2613 if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;2623 if (enum_type.tag_ty.toType().hasRuntimeBits(mod)) return null;
26142624
2615 switch (enum_type.names.len) {2625 switch (enum_type.names.len) {
2616 0 => return Value.@"unreachable",2626 0 => {
2627 const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
2628 return only.toValue();
2629 },
2617 1 => {2630 1 => {
2618 if (enum_type.values.len == 0) {2631 if (enum_type.values.len == 0) {
2619 const only = try mod.intern(.{ .enum_tag = .{2632 const only = try mod.intern(.{ .enum_tag = .{
...@@ -2645,6 +2658,7 @@ pub const Type = struct {...@@ -2645,6 +2658,7 @@ pub const Type = struct {
2645 .error_union,2658 .error_union,
2646 .enum_literal,2659 .enum_literal,
2647 .enum_tag,2660 .enum_tag,
2661 .empty_enum_value,
2648 .float,2662 .float,
2649 .ptr,2663 .ptr,
2650 .opt,2664 .opt,
...@@ -2790,6 +2804,7 @@ pub const Type = struct {...@@ -2790,6 +2804,7 @@ pub const Type = struct {
2790 .error_union,2804 .error_union,
2791 .enum_literal,2805 .enum_literal,
2792 .enum_tag,2806 .enum_tag,
2807 .empty_enum_value,
2793 .float,2808 .float,
2794 .ptr,2809 .ptr,
2795 .opt,2810 .opt,
src/value.zig+1
...@@ -441,6 +441,7 @@ pub const Value = struct {...@@ -441,6 +441,7 @@ pub const Value = struct {
441 .err,441 .err,
442 .enum_literal,442 .enum_literal,
443 .enum_tag,443 .enum_tag,
444 .empty_enum_value,
444 .float,445 .float,
445 => val,446 => val,
446447