Commit d0c022f734
Changed files (5)
src/codegen/llvm.zig
@@ -3548,12 +3548,11 @@ pub const Object = struct {
);
return ty;
},
- .opaque_type => |opaque_type| {
+ .opaque_type => {
const gop = try o.type_map.getOrPut(o.gpa, t.toIntern());
if (!gop.found_existing) {
- const name = try o.builder.string(ip.stringToSlice(
- try mod.opaqueFullyQualifiedName(opaque_type),
- ));
+ const decl = mod.declPtr(ip.loadOpaqueType(t.toIntern()).decl);
+ const name = try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod)));
gop.value_ptr.* = try o.builder.opaqueType(name);
}
return gop.value_ptr.*;
src/InternPool.zig
@@ -344,6 +344,7 @@ const KeyAdapter = struct {
pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool {
_ = b_void;
+ if (ctx.intern_pool.items.items(.tag)[b_map_index] == .removed) return false;
return ctx.intern_pool.indexToKey(@as(Index, @enumFromInt(b_map_index))).eql(a, ctx.intern_pool);
}
@@ -551,14 +552,14 @@ pub const Key = union(enum) {
/// This represents a struct that has been explicitly declared in source code,
/// or was created with `@Type`. It is unique and based on a declaration.
/// It may be a tuple, if declared like this: `struct {A, B, C}`.
- struct_type: StructType,
+ struct_type: NamespaceType,
/// This is an anonymous struct or tuple type which has no corresponding
/// declaration. It is used for types that have no `struct` keyword in the
/// source code, and were not created via `@Type`.
anon_struct_type: AnonStructType,
- union_type: Key.UnionType,
- opaque_type: OpaqueType,
- enum_type: EnumType,
+ union_type: NamespaceType,
+ opaque_type: NamespaceType,
+ enum_type: NamespaceType,
func_type: FuncType,
error_set_type: ErrorSetType,
/// The payload is the function body, either a `func_decl` or `func_instance`.
@@ -703,66 +704,41 @@ pub const Key = union(enum) {
}
};
- /// This is the hashmap key. To fetch other data associated with the struct, see `loadStructType`.
- pub const StructType = struct {
- /// The struct's owner Decl. `none` when the struct is `@TypeOf(.{})`.
- decl: OptionalDeclIndex,
- };
-
- /// This is the hashmap key. To fetch other data associated with the opaque, see `loadOpaqueType`.
- pub const OpaqueType = struct {
- /// The opaque's owner Decl.
- decl: DeclIndex,
- };
-
- /// This is the hashmap key. To fetch other data associated with the union, see `loadUnionType`.
- pub const UnionType = struct {
- /// The union's owner Decl.
- decl: DeclIndex,
- };
-
- /// This is the hashmap key. To fetch other data associated with the enum, see `loadEnumType`.
- pub const EnumType = struct {
- /// The enum's owner Decl.
- decl: DeclIndex,
- };
-
- pub const IncompleteEnumType = struct {
- /// Same as corresponding `EnumType` field.
- decl: DeclIndex,
- /// Same as corresponding `EnumType` field.
- namespace: OptionalNamespaceIndex,
- /// The field names and field values are not known yet, but
- /// the number of fields must be known ahead of time.
- fields_len: u32,
- /// This information is needed so that the size does not change
- /// later when populating field values.
- has_values: bool,
- /// Same as corresponding `EnumType` field.
- tag_mode: LoadedEnumType.TagMode,
- /// This may be updated via `setTagType` later.
- tag_ty: Index = .none,
- zir_index: TrackedInst.Index.Optional,
- captures: []const CaptureValue,
-
- pub fn toEnumType(self: @This()) LoadedEnumType {
- if (true) @compileError("AHHHH");
- return .{
- .decl = self.decl,
- .namespace = self.namespace,
- .tag_ty = self.tag_ty,
- .tag_mode = self.tag_mode,
- .names = .{ .start = 0, .len = 0 },
- .values = .{ .start = 0, .len = 0 },
- .zir_index = self.zir_index,
- };
- }
-
- /// Only the decl is used for hashing and equality, so we can construct
- /// this minimal key for use with `map`.
- pub fn toKey(self: @This()) Key {
- return .{ .enum_type = .{ .decl = self.decl } };
- }
+ /// This is the hashmap key. To fetch other data associated with the type, see:
+ /// * `loadStructType`
+ /// * `loadUnionType`
+ /// * `loadEnumType`
+ /// * `loadOpaqueType`
+ pub const NamespaceType = union(enum) {
+ /// This type corresponds to an actual source declaration, e.g. `struct { ... }`.
+ /// It is hashed based on its ZIR instruction index and set of captures.
+ declared: struct {
+ /// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction.
+ zir_index: TrackedInst.Index,
+ /// The captured values of this type. These values must be fully resolved per the language spec.
+ captures: union(enum) {
+ owned: CaptureValue.Slice,
+ external: []const CaptureValue,
+ },
+ },
+ /// This type is an automatically-generated enum tag type for a union.
+ /// It is hashed based on the index of the union type it corresponds to.
+ generated_tag: struct {
+ /// The union for which this is a tag type.
+ union_type: Index,
+ },
+ /// This type originates from a reification via `@Type`.
+ /// It is hased based on its ZIR instruction index and fields, attributes, etc.
+ /// To avoid making this key overly complex, the type-specific data is hased by Sema.
+ reified: struct {
+ /// A `reify` instruction.
+ zir_index: TrackedInst.Index,
+ /// A hash of this type's attributes, fields, etc, generated by Sema.
+ type_hash: u64,
+ },
+ /// This type is `@TypeOf(.{})`.
+ /// TODO: can we change the language spec to not special-case this type?
+ empty_struct: void,
};
pub const FuncType = struct {
@@ -1113,12 +1089,37 @@ pub const Key = union(enum) {
.payload => |y| Hash.hash(seed + 1, asBytes(&x.ty) ++ asBytes(&y)),
},
- inline .opaque_type,
+ .variable => |variable| Hash.hash(seed, asBytes(&variable.decl)),
+
+ .opaque_type,
.enum_type,
- .variable,
.union_type,
.struct_type,
- => |x| Hash.hash(seed, asBytes(&x.decl)),
+ => |namespace_type| {
+ var hasher = Hash.init(seed);
+ std.hash.autoHash(&hasher, std.meta.activeTag(namespace_type));
+ switch (namespace_type) {
+ .declared => |declared| {
+ std.hash.autoHash(&hasher, declared.zir_index);
+ const captures = switch (declared.captures) {
+ .owned => |cvs| cvs.get(ip),
+ .external => |cvs| cvs,
+ };
+ for (captures) |cv| {
+ std.hash.autoHash(&hasher, cv);
+ }
+ },
+ .generated_tag => |generated_tag| {
+ std.hash.autoHash(&hasher, generated_tag.union_type);
+ },
+ .reified => |reified| {
+ std.hash.autoHash(&hasher, reified.zir_index);
+ std.hash.autoHash(&hasher, reified.type_hash);
+ },
+ .empty_struct => {},
+ }
+ return hasher.final();
+ },
.int => |int| {
var hasher = Hash.init(seed);
@@ -1523,21 +1524,31 @@ pub const Key = union(enum) {
}
},
- .opaque_type => |a_info| {
- const b_info = b.opaque_type;
- return a_info.decl == b_info.decl;
- },
- .enum_type => |a_info| {
- const b_info = b.enum_type;
- return a_info.decl == b_info.decl;
- },
- .union_type => |a_info| {
- const b_info = b.union_type;
- return a_info.decl == b_info.decl;
- },
- .struct_type => |a_info| {
- const b_info = b.struct_type;
- return a_info.decl == b_info.decl;
+ inline .opaque_type, .enum_type, .union_type, .struct_type => |a_info, a_tag_ct| {
+ const b_info = @field(b, @tagName(a_tag_ct));
+ if (std.meta.activeTag(a_info) != b_info) return false;
+ switch (a_info) {
+ .declared => |a_d| {
+ const b_d = b_info.declared;
+ if (a_d.zir_index != b_d.zir_index) return false;
+ const a_captures = switch (a_d.captures) {
+ .owned => |s| s.get(ip),
+ .external => |cvs| cvs,
+ };
+ const b_captures = switch (b_d.captures) {
+ .owned => |s| s.get(ip),
+ .external => |cvs| cvs,
+ };
+ return std.mem.eql(u32, @ptrCast(a_captures), @ptrCast(b_captures));
+ },
+ .generated_tag => |a_gt| return a_gt.union_type == b_info.generated_tag.union_type,
+ .reified => |a_r| {
+ const b_r = b_info.reified;
+ return a_r.zir_index == b_r.zir_index and
+ a_r.type_hash == b_r.type_hash;
+ },
+ .empty_struct => return true,
+ }
},
.aggregate => |a_info| {
const b_info = b.aggregate;
@@ -1685,7 +1696,7 @@ pub const LoadedUnionType = struct {
/// The Decl that corresponds to the union itself.
decl: DeclIndex,
/// Represents the declarations inside this union.
- namespace: NamespaceIndex,
+ namespace: OptionalNamespaceIndex,
/// The enum tag type.
enum_tag_ty: Index,
/// List of field types in declaration order.
@@ -1695,8 +1706,8 @@ pub const LoadedUnionType = struct {
/// `none` means the ABI alignment of the type.
/// If this slice has length 0 it means all elements are `none`.
field_aligns: Alignment.Slice,
- /// Index of the union_decl ZIR instruction.
- zir_index: TrackedInst.Index.Optional,
+ /// Index of the union_decl or reify ZIR instruction.
+ zir_index: TrackedInst.Index,
captures: CaptureValue.Slice,
pub const RuntimeTag = enum(u2) {
@@ -1845,6 +1856,9 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
.len = captures_len,
};
extra_index += captures_len;
+ if (type_union.data.flags.is_reified) {
+ extra_index += 2; // PackedU64
+ }
const field_types: Index.Slice = .{
.start = extra_index,
@@ -1880,7 +1894,8 @@ pub const LoadedStructType = struct {
decl: OptionalDeclIndex,
/// `none` when the struct has no declarations.
namespace: OptionalNamespaceIndex,
- /// Index of the `struct_decl` ZIR instruction.
+ /// Index of the `struct_decl` or `reify` ZIR instruction.
+ /// Only `none` when the struct is `@TypeOf(.{})`.
zir_index: TrackedInst.Index.Optional,
layout: std.builtin.Type.ContainerLayout,
field_names: NullTerminatedString.Slice,
@@ -2239,6 +2254,9 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.len = captures_len,
};
extra_index += captures_len;
+ if (extra.data.flags.is_reified) {
+ extra_index += 2; // PackedU64
+ }
const field_types: Index.Slice = .{
.start = extra_index,
.len = fields_len,
@@ -2286,7 +2304,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.extra_index = item.data,
.decl = extra.data.decl.toOptional(),
.namespace = namespace,
- .zir_index = extra.data.zir_index,
+ .zir_index = extra.data.zir_index.toOptional(),
.layout = if (extra.data.flags.is_extern) .Extern else .Auto,
.field_names = names,
.field_types = field_types,
@@ -2314,6 +2332,9 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.len = captures_len,
};
extra_index += captures_len;
+ if (extra.data.flags.is_reified) {
+ extra_index += 2; // PackedU64
+ }
const field_types: Index.Slice = .{
.start = extra_index,
.len = fields_len,
@@ -2336,7 +2357,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.extra_index = item.data,
.decl = extra.data.decl.toOptional(),
.namespace = extra.data.namespace,
- .zir_index = extra.data.zir_index,
+ .zir_index = extra.data.zir_index.toOptional(),
.layout = .Packed,
.field_names = field_names,
.field_types = field_types,
@@ -2372,6 +2393,7 @@ const LoadedEnumType = struct {
names_map: MapIndex,
/// This is guaranteed to not be `.none` if explicit values are provided.
values_map: OptionalMapIndex,
+ /// This is `none` only if this is a generated tag type.
zir_index: TrackedInst.Index.Optional,
captures: CaptureValue.Slice,
@@ -2425,15 +2447,23 @@ const LoadedEnumType = struct {
pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
const item = ip.items.get(@intFromEnum(index));
- switch (item.tag) {
+ const tag_mode: LoadedEnumType.TagMode = switch (item.tag) {
.type_enum_auto => {
const extra = ip.extraDataTrail(EnumAuto, item.data);
+ var extra_index: u32 = @intCast(extra.end);
+ if (extra.data.zir_index == .none) {
+ extra_index += 1; // owner_union
+ }
+ const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
+ extra_index += 2; // type_hash: PackedU64
+ break :c 0;
+ } else extra.data.captures_len;
return .{
.decl = extra.data.decl,
.namespace = extra.data.namespace,
.tag_ty = extra.data.int_tag_type,
.names = .{
- .start = @intCast(extra.end + extra.data.captures_len),
+ .start = extra_index + captures_len,
.len = extra.data.fields_len,
},
.values = .{ .start = 0, .len = 0 },
@@ -2442,41 +2472,45 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
.values_map = .none,
.zir_index = extra.data.zir_index,
.captures = .{
- .start = @intCast(extra.end),
- .len = extra.data.captures_len,
- },
- };
- },
- .type_enum_explicit, .type_enum_nonexhaustive => {
- const extra = ip.extraDataTrail(EnumExplicit, item.data);
- return .{
- .decl = extra.data.decl,
- .namespace = extra.data.namespace,
- .tag_ty = extra.data.int_tag_type,
- .names = .{
- .start = @intCast(extra.end + extra.data.captures_len),
- .len = extra.data.fields_len,
- },
- .values = .{
- .start = @intCast(extra.end + extra.data.captures_len + extra.data.fields_len),
- .len = if (extra.data.values_map != .none) extra.data.fields_len else 0,
- },
- .tag_mode = switch (item.tag) {
- .type_enum_explicit => .explicit,
- .type_enum_nonexhaustive => .nonexhaustive,
- else => unreachable,
- },
- .names_map = extra.data.names_map,
- .values_map = extra.data.values_map,
- .zir_index = extra.data.zir_index,
- .captures = .{
- .start = @intCast(extra.end),
- .len = extra.data.captures_len,
+ .start = extra_index,
+ .len = captures_len,
},
};
},
+ .type_enum_explicit => .explicit,
+ .type_enum_nonexhaustive => .nonexhaustive,
else => unreachable,
- }
+ };
+ const extra = ip.extraDataTrail(EnumExplicit, item.data);
+ var extra_index: u32 = @intCast(extra.end);
+ if (extra.data.zir_index == .none) {
+ extra_index += 1; // owner_union
+ }
+ const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
+ extra_index += 2; // type_hash: PackedU64
+ break :c 0;
+ } else extra.data.captures_len;
+ return .{
+ .decl = extra.data.decl,
+ .namespace = extra.data.namespace,
+ .tag_ty = extra.data.int_tag_type,
+ .names = .{
+ .start = extra_index + captures_len,
+ .len = extra.data.fields_len,
+ },
+ .values = .{
+ .start = extra_index + captures_len + extra.data.fields_len,
+ .len = if (extra.data.values_map != .none) extra.data.fields_len else 0,
+ },
+ .tag_mode = tag_mode,
+ .names_map = extra.data.names_map,
+ .values_map = extra.data.values_map,
+ .zir_index = extra.data.zir_index,
+ .captures = .{
+ .start = extra_index,
+ .len = captures_len,
+ },
+ };
}
/// Note that this type doubles as the payload for `Tag.type_opaque`.
@@ -2484,9 +2518,9 @@ pub const LoadedOpaqueType = struct {
/// The opaque's owner Decl.
decl: DeclIndex,
/// Contains the declarations inside this opaque.
- namespace: NamespaceIndex,
- /// The index of the `opaque_decl` instruction.
- zir_index: TrackedInst.Index.Optional,
+ namespace: OptionalNamespaceIndex,
+ /// Index of the `opaque_decl` or `reify` instruction.
+ zir_index: TrackedInst.Index,
captures: CaptureValue.Slice,
};
@@ -2494,13 +2528,17 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType {
assert(ip.items.items(.tag)[@intFromEnum(index)] == .type_opaque);
const extra_index = ip.items.items(.data)[@intFromEnum(index)];
const extra = ip.extraDataTrail(Tag.TypeOpaque, extra_index);
+ const captures_len = if (extra.data.captures_len == std.math.maxInt(u32))
+ 0
+ else
+ extra.data.captures_len;
return .{
.decl = extra.data.decl,
.namespace = extra.data.namespace,
.zir_index = extra.data.zir_index,
.captures = .{
.start = extra.end,
- .len = extra.data.captures_len,
+ .len = captures_len,
},
};
}
@@ -2693,6 +2731,7 @@ pub const Index = enum(u32) {
},
};
+ removed: void,
type_int_signed: struct { data: u32 },
type_int_unsigned: struct { data: u32 },
type_array_big: struct { data: *Array },
@@ -3100,6 +3139,12 @@ comptime {
}
pub const Tag = enum(u8) {
+ /// This special tag represents a value which was removed from this pool via
+ /// `InternPool.remove`. The item remains allocated to preserve indices, but
+ /// lookups will consider it not equal to any other item, and all queries
+ /// assert not this tag. `data` is unused.
+ removed,
+
/// An integer type.
/// data is number of bits
type_int_signed,
@@ -3367,6 +3412,7 @@ pub const Tag = enum(u8) {
fn Payload(comptime tag: Tag) type {
return switch (tag) {
+ .removed => unreachable,
.type_int_signed => unreachable,
.type_int_unsigned => unreachable,
.type_array_big => Array,
@@ -3544,8 +3590,9 @@ pub const Tag = enum(u8) {
/// Trailing:
/// 0. captures_len: u32 // if `any_captures`
/// 1. capture: CaptureValue // for each `captures_len`
- /// 2. field type: Index for each field; declaration order
- /// 3. field align: Alignment for each field; declaration order
+ /// 2. type_hash: PackedU64 // if `is_reified`
+ /// 3. field type: Index for each field; declaration order
+ /// 4. field align: Alignment for each field; declaration order
pub const TypeUnion = struct {
flags: Flags,
/// This could be provided through the tag type, but it is more convenient
@@ -3557,10 +3604,10 @@ pub const Tag = enum(u8) {
/// Only valid after .have_layout
padding: u32,
decl: DeclIndex,
- namespace: NamespaceIndex,
+ namespace: OptionalNamespaceIndex,
/// The enum that provides the list of field names and values.
tag_ty: Index,
- zir_index: TrackedInst.Index.Optional,
+ zir_index: TrackedInst.Index,
pub const Flags = packed struct(u32) {
any_captures: bool,
@@ -3573,19 +3620,21 @@ pub const Tag = enum(u8) {
assumed_runtime_bits: bool,
assumed_pointer_aligned: bool,
alignment: Alignment,
- _: u13 = 0,
+ is_reified: bool,
+ _: u12 = 0,
};
};
/// Trailing:
/// 0. captures_len: u32 // if `any_captures`
/// 1. capture: CaptureValue // for each `captures_len`
- /// 2. type: Index for each fields_len
- /// 3. name: NullTerminatedString for each fields_len
- /// 4. init: Index for each fields_len // if tag is type_struct_packed_inits
+ /// 2. type_hash: PackedU64 // if `is_reified`
+ /// 3. type: Index for each fields_len
+ /// 4. name: NullTerminatedString for each fields_len
+ /// 5. init: Index for each fields_len // if tag is type_struct_packed_inits
pub const TypeStructPacked = struct {
decl: DeclIndex,
- zir_index: TrackedInst.Index.Optional,
+ zir_index: TrackedInst.Index,
fields_len: u32,
namespace: OptionalNamespaceIndex,
backing_int_ty: Index,
@@ -3597,7 +3646,8 @@ pub const Tag = enum(u8) {
/// Dependency loop detection when resolving field inits.
field_inits_wip: bool,
inits_resolved: bool,
- _: u29 = 0,
+ is_reified: bool,
+ _: u28 = 0,
};
};
@@ -3618,24 +3668,25 @@ pub const Tag = enum(u8) {
/// Trailing:
/// 0. captures_len: u32 // if `any_captures`
/// 1. capture: CaptureValue // for each `captures_len`
- /// 2. type: Index for each field in declared order
- /// 3. if not is_tuple:
+ /// 2. type_hash: PackedU64 // if `is_reified`
+ /// 3. type: Index for each field in declared order
+ /// 4. if not is_tuple:
/// names_map: MapIndex,
/// name: NullTerminatedString // for each field in declared order
- /// 4. if any_default_inits:
+ /// 5. if any_default_inits:
/// init: Index // for each field in declared order
- /// 5. if has_namespace:
+ /// 6. if has_namespace:
/// namespace: NamespaceIndex
- /// 6. if any_aligned_fields:
+ /// 7. if any_aligned_fields:
/// align: Alignment // for each field in declared order
- /// 7. if any_comptime_fields:
+ /// 8. if any_comptime_fields:
/// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 0
- /// 8. if not is_extern:
+ /// 9. if not is_extern:
/// field_index: RuntimeOrder // for each field in runtime order
- /// 9. field_offset: u32 // for each field in declared order, undef until layout_resolved
+ /// 10. field_offset: u32 // for each field in declared order, undef until layout_resolved
pub const TypeStruct = struct {
decl: DeclIndex,
- zir_index: TrackedInst.Index.Optional,
+ zir_index: TrackedInst.Index,
fields_len: u32,
flags: Flags,
size: u32,
@@ -3670,8 +3721,8 @@ pub const Tag = enum(u8) {
// The types and all its fields have had their layout resolved. Even through pointer,
// which `layout_resolved` does not ensure.
fully_resolved: bool,
-
- _: u7 = 0,
+ is_reified: bool,
+ _: u6 = 0,
};
};
@@ -3681,9 +3732,10 @@ pub const Tag = enum(u8) {
/// The opaque's owner Decl.
decl: DeclIndex,
/// Contains the declarations inside this opaque.
- namespace: NamespaceIndex,
+ namespace: OptionalNamespaceIndex,
/// The index of the `opaque_decl` instruction.
- zir_index: TrackedInst.Index.Optional,
+ zir_index: TrackedInst.Index,
+ /// `std.math.maxInt(u32)` indicates this type is reified.
captures_len: u32,
};
};
@@ -3992,12 +4044,15 @@ pub const Array = struct {
};
/// Trailing:
-/// 0. capture: CaptureValue // for each `captures_len`
-/// 1. field name: NullTerminatedString for each fields_len; declaration order
-/// 2. tag value: Index for each fields_len; declaration order
+/// 0. owner_union: Index // if `zir_index == .none`
+/// 1. capture: CaptureValue // for each `captures_len`
+/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
+/// 3. field name: NullTerminatedString for each fields_len; declaration order
+/// 4. tag value: Index for each fields_len; declaration order
pub const EnumExplicit = struct {
/// The Decl that corresponds to the enum itself.
decl: DeclIndex,
+ /// `std.math.maxInt(u32)` indicates this type is reified.
captures_len: u32,
/// This may be `none` if there are no declarations.
namespace: OptionalNamespaceIndex,
@@ -4011,15 +4066,20 @@ pub const EnumExplicit = struct {
/// If this is `none`, it means the trailing tag values are absent because
/// they are auto-numbered.
values_map: OptionalMapIndex,
+ /// `none` means this is a generated tag type.
+ /// There will be a trailing union type for which this is a tag.
zir_index: TrackedInst.Index.Optional,
};
/// Trailing:
-/// 0. capture: CaptureValue // for each `captures_len`
-/// 1. field name: NullTerminatedString for each fields_len; declaration order
+/// 0. owner_union: Index // if `zir_index == .none`
+/// 1. capture: CaptureValue // for each `captures_len`
+/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
+/// 3. field name: NullTerminatedString for each fields_len; declaration order
pub const EnumAuto = struct {
/// The Decl that corresponds to the enum itself.
decl: DeclIndex,
+ /// `std.math.maxInt(u32)` indicates this type is reified.
captures_len: u32,
/// This may be `none` if there are no declarations.
namespace: OptionalNamespaceIndex,
@@ -4029,6 +4089,8 @@ pub const EnumAuto = struct {
fields_len: u32,
/// Maps field names to declaration index.
names_map: MapIndex,
+ /// `none` means this is a generated tag type.
+ /// There will be a trailing union type for which this is a tag.
zir_index: TrackedInst.Index.Optional,
};
@@ -4269,6 +4331,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
const item = ip.items.get(@intFromEnum(index));
const data = item.data;
return switch (item.tag) {
+ .removed => unreachable,
.type_int_signed => .{
.int_type = .{
.signedness = .signed,
@@ -4330,31 +4393,123 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.inferred_error_set_type = @enumFromInt(data),
},
- .type_opaque => .{ .opaque_type = .{
- .decl = ip.extraData(Tag.TypeOpaque, data).decl,
+ .type_opaque => .{ .opaque_type = ns: {
+ const extra = ip.extraDataTrail(Tag.TypeOpaque, data);
+ if (extra.data.captures_len == std.math.maxInt(u32)) {
+ break :ns .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = 0,
+ } };
+ }
+ break :ns .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .captures = .{ .owned = .{
+ .start = extra.end,
+ .len = extra.data.captures_len,
+ } },
+ } };
} },
- .type_struct => .{ .struct_type = if (data == 0) .{
- .decl = .none,
- } else .{
- .decl = ip.extraData(Tag.TypeStruct, data).decl.toOptional(),
+ .type_struct => .{ .struct_type = ns: {
+ if (data == 0) break :ns .empty_struct;
+ const extra = ip.extraDataTrail(Tag.TypeStruct, data);
+ if (extra.data.flags.is_reified) {
+ assert(!extra.data.flags.any_captures);
+ break :ns .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = ip.extraData(PackedU64, extra.end).get(),
+ } };
+ }
+ break :ns .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .captures = .{ .owned = if (extra.data.flags.any_captures) .{
+ .start = extra.end + 1,
+ .len = ip.extra.items[extra.end],
+ } else .{ .start = 0, .len = 0 } },
+ } };
} },
- .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = .{
- .decl = ip.extraData(Tag.TypeStructPacked, data).decl.toOptional(),
+ .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = ns: {
+ const extra = ip.extraDataTrail(Tag.TypeStructPacked, data);
+ if (extra.data.flags.is_reified) {
+ assert(!extra.data.flags.any_captures);
+ break :ns .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = ip.extraData(PackedU64, extra.end).get(),
+ } };
+ }
+ break :ns .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .captures = .{ .owned = if (extra.data.flags.any_captures) .{
+ .start = extra.end + 1,
+ .len = ip.extra.items[extra.end],
+ } else .{ .start = 0, .len = 0 } },
+ } };
} },
.type_struct_anon => .{ .anon_struct_type = extraTypeStructAnon(ip, data) },
.type_tuple_anon => .{ .anon_struct_type = extraTypeTupleAnon(ip, data) },
- .type_union => .{ .union_type = .{
- .decl = ip.extraData(Tag.TypeUnion, data).decl,
+ .type_union => .{ .union_type = ns: {
+ const extra = ip.extraDataTrail(Tag.TypeUnion, data);
+ if (extra.data.flags.is_reified) {
+ assert(!extra.data.flags.any_captures);
+ break :ns .{ .reified = .{
+ .zir_index = extra.data.zir_index,
+ .type_hash = ip.extraData(PackedU64, extra.end).get(),
+ } };
+ }
+ break :ns .{ .declared = .{
+ .zir_index = extra.data.zir_index,
+ .captures = .{ .owned = if (extra.data.flags.any_captures) .{
+ .start = extra.end + 1,
+ .len = ip.extra.items[extra.end],
+ } else .{ .start = 0, .len = 0 } },
+ } };
} },
- .type_enum_auto => .{ .enum_type = .{
- .decl = ip.extraData(EnumAuto, data).decl,
+ .type_enum_auto => .{ .enum_type = ns: {
+ const extra = ip.extraDataTrail(EnumAuto, data);
+ const zir_index = extra.data.zir_index.unwrap() orelse {
+ assert(extra.data.captures_len == 0);
+ break :ns .{ .generated_tag = .{
+ .union_type = @enumFromInt(ip.extra.items[extra.end]),
+ } };
+ };
+ if (extra.data.captures_len == std.math.maxInt(u32)) {
+ break :ns .{ .reified = .{
+ .zir_index = zir_index,
+ .type_hash = ip.extraData(PackedU64, extra.end).get(),
+ } };
+ }
+ break :ns .{ .declared = .{
+ .zir_index = zir_index,
+ .captures = .{ .owned = .{
+ .start = extra.end,
+ .len = extra.data.captures_len,
+ } },
+ } };
} },
- .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = .{
- .decl = ip.extraData(EnumExplicit, data).decl,
+ .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: {
+ const extra = ip.extraDataTrail(EnumExplicit, data);
+ const zir_index = extra.data.zir_index.unwrap() orelse {
+ assert(extra.data.captures_len == 0);
+ break :ns .{ .generated_tag = .{
+ .union_type = @enumFromInt(ip.extra.items[extra.end]),
+ } };
+ };
+ if (extra.data.captures_len == std.math.maxInt(u32)) {
+ break :ns .{ .reified = .{
+ .zir_index = zir_index,
+ .type_hash = ip.extraData(PackedU64, extra.end).get(),
+ } };
+ }
+ break :ns .{ .declared = .{
+ .zir_index = zir_index,
+ .captures = .{ .owned = .{
+ .start = extra.end,
+ .len = extra.data.captures_len,
+ } },
+ } };
} },
.type_function => .{ .func_type = ip.extraFuncType(data) },
@@ -4979,7 +5134,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
.union_type => unreachable, // use getUnionType() instead
.opaque_type => unreachable, // use getOpaqueType() instead
- .enum_type => unreachable, // use getEnum() or getIncompleteEnum() instead
+ .enum_type => unreachable, // use getEnumType() instead
.func_type => unreachable, // use getFuncType() instead
.extern_func => unreachable, // use getExternFunc() instead
.func => unreachable, // use getFuncInstance() or getFuncDecl() instead
@@ -5652,9 +5807,7 @@ pub const UnionTypeInit = struct {
assumed_pointer_aligned: bool,
alignment: Alignment,
},
- decl: DeclIndex,
- namespace: NamespaceIndex,
- zir_index: TrackedInst.Index.Optional,
+ has_namespace: bool,
fields_len: u32,
enum_tag_ty: Index,
/// May have length 0 which leaves the values unset until later.
@@ -5663,23 +5816,50 @@ pub const UnionTypeInit = struct {
/// The logic for `any_aligned_fields` is asserted to have been done before
/// calling this function.
field_aligns: []const Alignment,
- captures: []const CaptureValue,
+ key: union(enum) {
+ declared: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+ },
+ reified: struct {
+ zir_index: TrackedInst.Index,
+ type_hash: u64,
+ },
+ },
};
-pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocator.Error!Index {
- const prev_extra_len = ip.extra.items.len;
+pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocator.Error!WipNamespaceType.Result {
+ const adapter: KeyAdapter = .{ .intern_pool = ip };
+ const gop = try ip.map.getOrPutAdapted(gpa, Key{ .union_type = switch (ini.key) {
+ .declared => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .external = d.captures },
+ } },
+ .reified => |r| .{ .reified = .{
+ .zir_index = r.zir_index,
+ .type_hash = r.type_hash,
+ } },
+ } }, adapter);
+ if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) };
+ errdefer _ = ip.map.pop();
+
const align_elements_len = if (ini.flags.any_aligned_fields) (ini.fields_len + 3) / 4 else 0;
const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4);
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeUnion).Struct.fields.len +
- @intFromBool(ini.captures.len != 0) + // captures_len
- ini.captures.len + // captures
+ // TODO: fmt bug
+ // zig fmt: off
+ switch (ini.key) {
+ .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
+ .reified => 2, // type_hash: PackedU64
+ } +
+ // zig fmt: on
ini.fields_len + // field types
align_elements_len);
try ip.items.ensureUnusedCapacity(gpa, 1);
- const union_type_extra_index = ip.addExtraAssumeCapacity(Tag.TypeUnion{
+ const extra_index = ip.addExtraAssumeCapacity(Tag.TypeUnion{
.flags = .{
- .any_captures = ini.captures.len != 0,
+ .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,
.runtime_tag = ini.flags.runtime_tag,
.any_aligned_fields = ini.flags.any_aligned_fields,
.layout = ini.flags.layout,
@@ -5688,19 +5868,30 @@ pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocat
.assumed_runtime_bits = ini.flags.assumed_runtime_bits,
.assumed_pointer_aligned = ini.flags.assumed_pointer_aligned,
.alignment = ini.flags.alignment,
+ .is_reified = ini.key == .reified,
},
.fields_len = ini.fields_len,
.size = std.math.maxInt(u32),
.padding = std.math.maxInt(u32),
- .decl = ini.decl,
- .namespace = ini.namespace,
+ .decl = undefined, // set by `finish`
+ .namespace = .none, // set by `finish`
.tag_ty = ini.enum_tag_ty,
- .zir_index = ini.zir_index,
+ .zir_index = switch (ini.key) {
+ inline else => |x| x.zir_index,
+ },
+ });
+
+ ip.items.appendAssumeCapacity(.{
+ .tag = .type_union,
+ .data = extra_index,
});
- if (ini.captures.len != 0) {
- ip.extra.appendAssumeCapacity(@intCast(ini.captures.len));
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures));
+ switch (ini.key) {
+ .declared => |d| if (d.captures.len != 0) {
+ ip.extra.appendAssumeCapacity(@intCast(d.captures.len));
+ ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures));
+ },
+ .reified => |r| _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)),
}
// field types
@@ -5725,27 +5916,41 @@ pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocat
assert(ini.field_aligns.len == 0);
}
- const adapter: KeyAdapter = .{ .intern_pool = ip };
- const gop = try ip.map.getOrPutAdapted(gpa, Key{
- .union_type = .{ .decl = ini.decl },
- }, adapter);
- if (gop.found_existing) {
- ip.extra.items.len = prev_extra_len;
- return @enumFromInt(gop.index);
+ return .{ .wip = .{
+ .index = @enumFromInt(ip.items.len - 1),
+ .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "decl").?,
+ .namespace_extra_index = if (ini.has_namespace)
+ extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?
+ else
+ null,
+ } };
+}
+
+pub const WipNamespaceType = struct {
+ index: Index,
+ decl_extra_index: u32,
+ namespace_extra_index: ?u32,
+ pub fn finish(wip: WipNamespaceType, ip: *InternPool, decl: DeclIndex, namespace: OptionalNamespaceIndex) Index {
+ ip.extra.items[wip.decl_extra_index] = @intFromEnum(decl);
+ if (wip.namespace_extra_index) |i| {
+ ip.extra.items[i] = @intFromEnum(namespace.unwrap().?);
+ } else {
+ assert(namespace == .none);
+ }
+ return wip.index;
+ }
+ pub fn cancel(wip: WipNamespaceType, ip: *InternPool) void {
+ ip.remove(wip.index);
}
- ip.items.appendAssumeCapacity(.{
- .tag = .type_union,
- .data = union_type_extra_index,
- });
- return @enumFromInt(ip.items.len - 1);
-}
+ pub const Result = union(enum) {
+ wip: WipNamespaceType,
+ existing: Index,
+ };
+};
pub const StructTypeInit = struct {
- decl: DeclIndex,
- namespace: OptionalNamespaceIndex,
layout: std.builtin.Type.ContainerLayout,
- zir_index: TrackedInst.Index.Optional,
fields_len: u32,
known_non_opv: bool,
requires_comptime: RequiresComptime,
@@ -5754,61 +5959,101 @@ pub const StructTypeInit = struct {
any_default_inits: bool,
inits_resolved: bool,
any_aligned_fields: bool,
- captures: []const CaptureValue,
+ has_namespace: bool,
+ key: union(enum) {
+ declared: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+ },
+ reified: struct {
+ zir_index: TrackedInst.Index,
+ type_hash: u64,
+ },
+ },
};
pub fn getStructType(
ip: *InternPool,
gpa: Allocator,
ini: StructTypeInit,
-) Allocator.Error!Index {
+) Allocator.Error!WipNamespaceType.Result {
const adapter: KeyAdapter = .{ .intern_pool = ip };
- const key: Key = .{
- .struct_type = .{ .decl = ini.decl.toOptional() },
- };
+ const key: Key = .{ .struct_type = switch (ini.key) {
+ .declared => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .external = d.captures },
+ } },
+ .reified => |r| .{ .reified = .{
+ .zir_index = r.zir_index,
+ .type_hash = r.type_hash,
+ } },
+ } };
const gop = try ip.map.getOrPutAdapted(gpa, key, adapter);
- if (gop.found_existing) return @enumFromInt(gop.index);
+ if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) };
errdefer _ = ip.map.pop();
const names_map = try ip.addMap(gpa, ini.fields_len);
errdefer _ = ip.maps.pop();
+ const zir_index = switch (ini.key) {
+ inline else => |x| x.zir_index,
+ };
+
const is_extern = switch (ini.layout) {
.Auto => false,
.Extern => true,
.Packed => {
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStructPacked).Struct.fields.len +
- @intFromBool(ini.captures.len != 0) + // captures_len
- ini.captures.len + // captures
+ // TODO: fmt bug
+ // zig fmt: off
+ switch (ini.key) {
+ .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
+ .reified => 2, // type_hash: PackedU64
+ } +
+ // zig fmt: on
ini.fields_len + // types
ini.fields_len + // names
ini.fields_len); // inits
+ const extra_index = ip.addExtraAssumeCapacity(Tag.TypeStructPacked{
+ .decl = undefined, // set by `finish`
+ .zir_index = zir_index,
+ .fields_len = ini.fields_len,
+ .namespace = .none,
+ .backing_int_ty = .none,
+ .names_map = names_map,
+ .flags = .{
+ .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,
+ .field_inits_wip = false,
+ .inits_resolved = ini.inits_resolved,
+ .is_reified = ini.key == .reified,
+ },
+ });
try ip.items.append(gpa, .{
.tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed,
- .data = ip.addExtraAssumeCapacity(Tag.TypeStructPacked{
- .decl = ini.decl,
- .zir_index = ini.zir_index,
- .fields_len = ini.fields_len,
- .namespace = ini.namespace,
- .backing_int_ty = .none,
- .names_map = names_map,
- .flags = .{
- .any_captures = ini.captures.len != 0,
- .field_inits_wip = false,
- .inits_resolved = ini.inits_resolved,
- },
- }),
+ .data = extra_index,
});
- if (ini.captures.len != 0) {
- ip.extra.appendAssumeCapacity(@intCast(ini.captures.len));
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures));
+ switch (ini.key) {
+ .declared => |d| if (d.captures.len != 0) {
+ ip.extra.appendAssumeCapacity(@intCast(d.captures.len));
+ ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures));
+ },
+ .reified => |r| {
+ _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash));
+ },
}
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len);
if (ini.any_default_inits) {
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
}
- return @enumFromInt(ip.items.len - 1);
+ return .{ .wip = .{
+ .index = @enumFromInt(ip.items.len - 1),
+ .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "decl").?,
+ .namespace_extra_index = if (ini.has_namespace)
+ extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?
+ else
+ null,
+ } };
},
};
@@ -5817,44 +6062,56 @@ pub fn getStructType(
const comptime_elements_len = if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0;
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStruct).Struct.fields.len +
- @intFromBool(ini.captures.len != 0) + // captures_len
- ini.captures.len + // captures
+ // TODO: fmt bug
+ // zig fmt: off
+ switch (ini.key) {
+ .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
+ .reified => 2, // type_hash: PackedU64
+ } +
+ // zig fmt: on
(ini.fields_len * 5) + // types, names, inits, runtime order, offsets
align_elements_len + comptime_elements_len +
2); // names_map + namespace
+ const extra_index = ip.addExtraAssumeCapacity(Tag.TypeStruct{
+ .decl = undefined, // set by `finish`
+ .zir_index = zir_index,
+ .fields_len = ini.fields_len,
+ .size = std.math.maxInt(u32),
+ .flags = .{
+ .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,
+ .is_extern = is_extern,
+ .known_non_opv = ini.known_non_opv,
+ .requires_comptime = ini.requires_comptime,
+ .is_tuple = ini.is_tuple,
+ .assumed_runtime_bits = false,
+ .assumed_pointer_aligned = false,
+ .has_namespace = ini.has_namespace,
+ .any_comptime_fields = ini.any_comptime_fields,
+ .any_default_inits = ini.any_default_inits,
+ .any_aligned_fields = ini.any_aligned_fields,
+ .alignment = .none,
+ .alignment_wip = false,
+ .field_types_wip = false,
+ .layout_wip = false,
+ .layout_resolved = false,
+ .field_inits_wip = false,
+ .inits_resolved = ini.inits_resolved,
+ .fully_resolved = false,
+ .is_reified = ini.key == .reified,
+ },
+ });
try ip.items.append(gpa, .{
.tag = .type_struct,
- .data = ip.addExtraAssumeCapacity(Tag.TypeStruct{
- .decl = ini.decl,
- .zir_index = ini.zir_index,
- .fields_len = ini.fields_len,
- .size = std.math.maxInt(u32),
- .flags = .{
- .any_captures = ini.captures.len != 0,
- .is_extern = is_extern,
- .known_non_opv = ini.known_non_opv,
- .requires_comptime = ini.requires_comptime,
- .is_tuple = ini.is_tuple,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
- .has_namespace = ini.namespace != .none,
- .any_comptime_fields = ini.any_comptime_fields,
- .any_default_inits = ini.any_default_inits,
- .any_aligned_fields = ini.any_aligned_fields,
- .alignment = .none,
- .alignment_wip = false,
- .field_types_wip = false,
- .layout_wip = false,
- .layout_resolved = false,
- .field_inits_wip = false,
- .inits_resolved = ini.inits_resolved,
- .fully_resolved = false,
- },
- }),
+ .data = extra_index,
});
- if (ini.captures.len != 0) {
- ip.extra.appendAssumeCapacity(@intCast(ini.captures.len));
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures));
+ switch (ini.key) {
+ .declared => |d| if (d.captures.len != 0) {
+ ip.extra.appendAssumeCapacity(@intCast(d.captures.len));
+ ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures));
+ },
+ .reified => |r| {
+ _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash));
+ },
}
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
if (!ini.is_tuple) {
@@ -5864,9 +6121,10 @@ pub fn getStructType(
if (ini.any_default_inits) {
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
}
- if (ini.namespace.unwrap()) |namespace| {
- ip.extra.appendAssumeCapacity(@intFromEnum(namespace));
- }
+ const namespace_extra_index: ?u32 = if (ini.has_namespace) i: {
+ ip.extra.appendAssumeCapacity(undefined); // set by `finish`
+ break :i @intCast(ip.extra.items.len - 1);
+ } else null;
if (ini.any_aligned_fields) {
ip.extra.appendNTimesAssumeCapacity(align_element, align_elements_len);
}
@@ -5877,7 +6135,11 @@ pub fn getStructType(
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(LoadedStructType.RuntimeOrder.unresolved), ini.fields_len);
}
ip.extra.appendNTimesAssumeCapacity(std.math.maxInt(u32), ini.fields_len);
- return @enumFromInt(ip.items.len - 1);
+ return .{ .wip = .{
+ .index = @enumFromInt(ip.items.len - 1),
+ .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "decl").?,
+ .namespace_extra_index = namespace_extra_index,
+ } };
}
pub const AnonStructTypeInit = struct {
@@ -6513,282 +6775,399 @@ fn finishFuncInstance(
return func_index;
}
-/// Provides API for completing an enum type after calling `getIncompleteEnum`.
-pub const IncompleteEnumType = struct {
+pub const EnumTypeInit = struct {
+ has_namespace: bool,
+ has_values: bool,
+ tag_mode: LoadedEnumType.TagMode,
+ fields_len: u32,
+ key: union(enum) {
+ declared: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+ },
+ reified: struct {
+ zir_index: TrackedInst.Index,
+ type_hash: u64,
+ },
+ },
+};
+
+pub const WipEnumType = struct {
index: Index,
tag_ty_index: u32,
+ decl_index: u32,
+ namespace_index: ?u32,
names_map: MapIndex,
names_start: u32,
values_map: OptionalMapIndex,
values_start: u32,
+ expected_fields_len: if (std.debug.runtime_safety) u32 else void,
- pub fn setTagType(self: @This(), ip: *InternPool, tag_ty: Index) void {
- assert(tag_ty == .noreturn_type or ip.isIntegerType(tag_ty));
- ip.extra.items[self.tag_ty_index] = @intFromEnum(tag_ty);
- }
-
- /// Returns the already-existing field with the same name, if any.
- pub fn addFieldName(
- self: @This(),
+ pub fn prepare(
+ wip: WipEnumType,
ip: *InternPool,
- name: NullTerminatedString,
- ) ?u32 {
- return ip.addFieldName(self.names_map, self.names_start, name);
+ decl: DeclIndex,
+ namespace: OptionalNamespaceIndex,
+ tag_ty: Index,
+ ) void {
+ assert(ip.isIntegerType(tag_ty));
+ ip.extra.items[wip.tag_ty_index] = @intFromEnum(tag_ty);
+ ip.extra.items[wip.decl_index] = @intFromEnum(decl);
+ if (wip.namespace_index) |i| {
+ ip.extra.items[i] = @intFromEnum(namespace.unwrap().?);
+ } else {
+ assert(namespace == .none);
+ }
}
- /// Returns the already-existing field with the same value, if any.
- /// Make sure the type of the value has the integer tag type of the enum.
- pub fn addFieldValue(
- self: @This(),
- ip: *InternPool,
- value: Index,
- ) ?u32 {
- assert(ip.typeOf(value) == @as(Index, @enumFromInt(ip.extra.items[self.tag_ty_index])));
- const map = &ip.maps.items[@intFromEnum(self.values_map.unwrap().?)];
+ pub const FieldConflict = struct {
+ kind: enum { name, value },
+ prev_field_idx: u32,
+ };
+
+ /// Returns the already-existing field with the same name or value, if any.
+ /// If the enum is automatially numbered, `value` must be `.none`.
+ /// Otherwise, the type of `value` must be the integer tag type of the enum.
+ pub fn nextField(wip: WipEnumType, ip: *InternPool, name: NullTerminatedString, value: Index) ?FieldConflict {
+ if (ip.addFieldName(wip.names_map, wip.names_start, name)) |conflict| {
+ return .{ .kind = .name, .prev_field_idx = conflict };
+ }
+ if (value == .none) {
+ assert(wip.values_map == .none);
+ return null;
+ }
+ assert(ip.typeOf(value) == @as(Index, @enumFromInt(ip.extra.items[wip.tag_ty_index])));
+ const map = &ip.maps.items[@intFromEnum(wip.values_map.unwrap().?)];
const field_index = map.count();
- const indexes = ip.extra.items[self.values_start..][0..field_index];
+ const indexes = ip.extra.items[wip.values_start..][0..field_index];
const adapter: Index.Adapter = .{ .indexes = @ptrCast(indexes) };
const gop = map.getOrPutAssumeCapacityAdapted(value, adapter);
- if (gop.found_existing) return @intCast(gop.index);
- ip.extra.items[self.values_start + field_index] = @intFromEnum(value);
+ if (gop.found_existing) {
+ return .{ .kind = .value, .prev_field_idx = @intCast(gop.index) };
+ }
+ ip.extra.items[wip.values_start + field_index] = @intFromEnum(value);
return null;
}
-};
-/// This is used to create an enum type in the `InternPool`, with the ability
-/// to update the tag type, field names, and field values later.
-pub fn getIncompleteEnum(
- ip: *InternPool,
- gpa: Allocator,
- enum_type: Key.IncompleteEnumType,
-) Allocator.Error!IncompleteEnumType {
- switch (enum_type.tag_mode) {
- .auto => return getIncompleteEnumAuto(ip, gpa, enum_type),
- .explicit => return getIncompleteEnumExplicit(ip, gpa, enum_type, .type_enum_explicit),
- .nonexhaustive => return getIncompleteEnumExplicit(ip, gpa, enum_type, .type_enum_nonexhaustive),
+ pub fn finish(wip: WipEnumType, ip: *InternPool) Index {
+ if (std.debug.runtime_safety) {
+ const names_map = &ip.maps.items[@intFromEnum(wip.names_map)];
+ assert(names_map.count() == wip.expected_fields_len);
+ if (wip.values_map.unwrap()) |v| {
+ const values_map = &ip.maps.items[@intFromEnum(v)];
+ assert(values_map.count() == wip.expected_fields_len);
+ }
+ }
+ return wip.index;
}
-}
-fn getIncompleteEnumAuto(
- ip: *InternPool,
- gpa: Allocator,
- enum_type: Key.IncompleteEnumType,
-) Allocator.Error!IncompleteEnumType {
- const int_tag_type = if (enum_type.tag_ty != .none)
- enum_type.tag_ty
- else
- try ip.get(gpa, .{ .int_type = .{
- .bits = if (enum_type.fields_len == 0) 0 else std.math.log2_int_ceil(u32, enum_type.fields_len),
- .signedness = .unsigned,
- } });
-
- // We must keep the map in sync with `items`. The hash and equality functions
- // for enum types only look at the decl field, which is present even in
- // an `IncompleteEnumType`.
- const adapter: KeyAdapter = .{ .intern_pool = ip };
- const gop = try ip.map.getOrPutAdapted(gpa, enum_type.toKey(), adapter);
- assert(!gop.found_existing);
-
- const names_map = try ip.addMap(gpa, enum_type.fields_len);
-
- const extra_fields_len: u32 = @typeInfo(EnumAuto).Struct.fields.len;
- try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + enum_type.captures.len + enum_type.fields_len);
- try ip.items.ensureUnusedCapacity(gpa, 1);
-
- const extra_index = ip.addExtraAssumeCapacity(EnumAuto{
- .decl = enum_type.decl,
- .captures_len = @intCast(enum_type.captures.len),
- .namespace = enum_type.namespace,
- .int_tag_type = int_tag_type,
- .names_map = names_map,
- .fields_len = enum_type.fields_len,
- .zir_index = enum_type.zir_index,
- });
+ pub fn cancel(wip: WipEnumType, ip: *InternPool) void {
+ ip.remove(wip.index);
+ }
- ip.items.appendAssumeCapacity(.{
- .tag = .type_enum_auto,
- .data = extra_index,
- });
- ip.extra.appendSliceAssumeCapacity(@ptrCast(enum_type.captures));
- ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), enum_type.fields_len);
- return .{
- .index = @enumFromInt(ip.items.len - 1),
- .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?,
- .names_map = names_map,
- .names_start = extra_index + extra_fields_len,
- .values_map = .none,
- .values_start = undefined,
+ pub const Result = union(enum) {
+ wip: WipEnumType,
+ existing: Index,
};
-}
+};
-fn getIncompleteEnumExplicit(
+pub fn getEnumType(
ip: *InternPool,
gpa: Allocator,
- enum_type: Key.IncompleteEnumType,
- tag: Tag,
-) Allocator.Error!IncompleteEnumType {
- // We must keep the map in sync with `items`. The hash and equality functions
- // for enum types only look at the decl field, which is present even in
- // an `IncompleteEnumType`.
+ ini: EnumTypeInit,
+) Allocator.Error!WipEnumType.Result {
const adapter: KeyAdapter = .{ .intern_pool = ip };
- const gop = try ip.map.getOrPutAdapted(gpa, enum_type.toKey(), adapter);
- assert(!gop.found_existing);
-
- const names_map = try ip.addMap(gpa, enum_type.fields_len);
- const values_map: OptionalMapIndex = if (!enum_type.has_values) .none else m: {
- const values_map = try ip.addMap(gpa, enum_type.fields_len);
- break :m values_map.toOptional();
- };
-
- const reserved_len = enum_type.fields_len +
- if (enum_type.has_values) enum_type.fields_len else 0;
+ const gop = try ip.map.getOrPutAdapted(gpa, Key{ .enum_type = switch (ini.key) {
+ .declared => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .external = d.captures },
+ } },
+ .reified => |r| .{ .reified = .{
+ .zir_index = r.zir_index,
+ .type_hash = r.type_hash,
+ } },
+ } }, adapter);
+ if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) };
+ assert(gop.index == ip.items.len);
+ errdefer _ = ip.map.pop();
- const extra_fields_len: u32 = @typeInfo(EnumExplicit).Struct.fields.len;
- try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + enum_type.captures.len + reserved_len);
try ip.items.ensureUnusedCapacity(gpa, 1);
- const extra_index = ip.addExtraAssumeCapacity(EnumExplicit{
- .decl = enum_type.decl,
- .captures_len = @intCast(enum_type.captures.len),
- .namespace = enum_type.namespace,
- .int_tag_type = enum_type.tag_ty,
- .fields_len = enum_type.fields_len,
- .names_map = names_map,
- .values_map = values_map,
- .zir_index = enum_type.zir_index,
- });
+ const names_map = try ip.addMap(gpa, ini.fields_len);
+ errdefer _ = ip.maps.pop();
- ip.items.appendAssumeCapacity(.{
- .tag = tag,
- .data = extra_index,
- });
- ip.extra.appendSliceAssumeCapacity(@ptrCast(enum_type.captures));
- // This is both fields and values (if present).
- ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), reserved_len);
- return .{
- .index = @enumFromInt(ip.items.len - 1),
- .tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?,
- .names_map = names_map,
- .names_start = extra_index + extra_fields_len,
- .values_map = values_map,
- .values_start = extra_index + extra_fields_len + enum_type.fields_len,
- };
+ switch (ini.tag_mode) {
+ .auto => {
+ assert(!ini.has_values);
+ try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len +
+ // TODO: fmt bug
+ // zig fmt: off
+ switch (ini.key) {
+ .declared => |d| d.captures.len,
+ .reified => 2, // type_hash: PackedU64
+ } +
+ // zig fmt: on
+ ini.fields_len); // field types
+
+ const extra_index = ip.addExtraAssumeCapacity(EnumAuto{
+ .decl = undefined, // set by `prepare`
+ .captures_len = switch (ini.key) {
+ .declared => |d| @intCast(d.captures.len),
+ .reified => std.math.maxInt(u32),
+ },
+ .namespace = .none,
+ .int_tag_type = .none, // set by `prepare`
+ .fields_len = ini.fields_len,
+ .names_map = names_map,
+ .zir_index = switch (ini.key) {
+ inline else => |x| x.zir_index,
+ }.toOptional(),
+ });
+ ip.items.appendAssumeCapacity(.{
+ .tag = .type_enum_auto,
+ .data = extra_index,
+ });
+ switch (ini.key) {
+ .declared => |d| ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)),
+ .reified => |r| _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)),
+ }
+ const names_start = ip.extra.items.len;
+ ip.extra.appendNTimesAssumeCapacity(undefined, ini.fields_len);
+ return .{ .wip = .{
+ .index = @enumFromInt(gop.index),
+ .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?,
+ .decl_index = extra_index + std.meta.fieldIndex(EnumAuto, "decl").?,
+ .namespace_index = if (ini.has_namespace) extra_index + std.meta.fieldIndex(EnumAuto, "namespace").? else null,
+ .names_map = names_map,
+ .names_start = @intCast(names_start),
+ .values_map = .none,
+ .values_start = undefined,
+ .expected_fields_len = if (std.debug.runtime_safety) ini.fields_len else {},
+ } };
+ },
+ .explicit, .nonexhaustive => {
+ const values_map: OptionalMapIndex = if (!ini.has_values) .none else m: {
+ const values_map = try ip.addMap(gpa, ini.fields_len);
+ break :m values_map.toOptional();
+ };
+ errdefer if (ini.has_values) {
+ _ = ip.map.pop();
+ };
+
+ try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len +
+ // TODO: fmt bug
+ // zig fmt: off
+ switch (ini.key) {
+ .declared => |d| d.captures.len,
+ .reified => 2, // type_hash: PackedU64
+ } +
+ // zig fmt: on
+ ini.fields_len + // field types
+ ini.fields_len * @intFromBool(ini.has_values)); // field values
+
+ const extra_index = ip.addExtraAssumeCapacity(EnumExplicit{
+ .decl = undefined, // set by `prepare`
+ .captures_len = switch (ini.key) {
+ .declared => |d| @intCast(d.captures.len),
+ .reified => std.math.maxInt(u32),
+ },
+ .namespace = .none,
+ .int_tag_type = .none, // set by `prepare`
+ .fields_len = ini.fields_len,
+ .names_map = names_map,
+ .values_map = values_map,
+ .zir_index = switch (ini.key) {
+ inline else => |x| x.zir_index,
+ }.toOptional(),
+ });
+ ip.items.appendAssumeCapacity(.{
+ .tag = switch (ini.tag_mode) {
+ .auto => unreachable,
+ .explicit => .type_enum_explicit,
+ .nonexhaustive => .type_enum_nonexhaustive,
+ },
+ .data = extra_index,
+ });
+ switch (ini.key) {
+ .declared => |d| ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)),
+ .reified => |r| _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)),
+ }
+ const names_start = ip.extra.items.len;
+ ip.extra.appendNTimesAssumeCapacity(undefined, ini.fields_len);
+ const values_start = ip.extra.items.len;
+ if (ini.has_values) {
+ ip.extra.appendNTimesAssumeCapacity(undefined, ini.fields_len);
+ }
+ return .{ .wip = .{
+ .index = @enumFromInt(gop.index),
+ .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?,
+ .decl_index = extra_index + std.meta.fieldIndex(EnumAuto, "decl").?,
+ .namespace_index = if (ini.has_namespace) extra_index + std.meta.fieldIndex(EnumAuto, "namespace").? else null,
+ .names_map = names_map,
+ .names_start = @intCast(names_start),
+ .values_map = values_map,
+ .values_start = @intCast(values_start),
+ .expected_fields_len = if (std.debug.runtime_safety) ini.fields_len else {},
+ } };
+ },
+ }
}
-pub const GetEnumInit = struct {
+const GeneratedTagEnumTypeInit = struct {
decl: DeclIndex,
- namespace: OptionalNamespaceIndex,
+ owner_union_ty: Index,
tag_ty: Index,
names: []const NullTerminatedString,
values: []const Index,
tag_mode: LoadedEnumType.TagMode,
- zir_index: TrackedInst.Index.Optional,
- captures: []const CaptureValue,
};
-pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Error!Index {
- const adapter: KeyAdapter = .{ .intern_pool = ip };
- const gop = try ip.map.getOrPutAdapted(gpa, Key{
- .enum_type = .{ .decl = ini.decl },
- }, adapter);
- if (gop.found_existing) return @enumFromInt(gop.index);
- errdefer _ = ip.map.pop();
+/// Creates an enum type which was automatically-generated as the tag type of a
+/// `union` with no explicit tag type. Since this is only called once per union
+/// type, it asserts that no matching type yet exists.
+pub fn getGeneratedTagEnumType(ip: *InternPool, gpa: Allocator, ini: GeneratedTagEnumTypeInit) Allocator.Error!Index {
+ assert(ip.isUnion(ini.owner_union_ty));
+ assert(ip.isIntegerType(ini.tag_ty));
+ for (ini.values) |val| assert(ip.typeOf(val) == ini.tag_ty);
+
+ try ip.map.ensureUnusedCapacity(gpa, 1);
try ip.items.ensureUnusedCapacity(gpa, 1);
- assert(ini.tag_ty == .noreturn_type or ip.isIntegerType(ini.tag_ty));
- for (ini.values) |value| assert(ip.typeOf(value) == ini.tag_ty);
+ const names_map = try ip.addMap(gpa, ini.names.len);
+ errdefer _ = ip.maps.pop();
+ ip.addStringsToMap(names_map, ini.names);
+
+ const fields_len: u32 = @intCast(ini.names.len);
switch (ini.tag_mode) {
.auto => {
- const names_map = try ip.addMap(gpa, ini.names.len);
- addStringsToMap(ip, names_map, ini.names);
-
- const fields_len: u32 = @intCast(ini.names.len);
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len +
- ini.captures.len + fields_len);
+ 1 + // owner_union
+ fields_len); // field names
ip.items.appendAssumeCapacity(.{
.tag = .type_enum_auto,
.data = ip.addExtraAssumeCapacity(EnumAuto{
.decl = ini.decl,
- .captures_len = @intCast(ini.captures.len),
- .namespace = ini.namespace,
+ .captures_len = 0,
+ .namespace = .none,
.int_tag_type = ini.tag_ty,
+ .fields_len = fields_len,
.names_map = names_map,
+ .zir_index = .none,
+ }),
+ });
+ ip.extra.appendAssumeCapacity(@intFromEnum(ini.owner_union_ty));
+ ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
+ },
+ .explicit, .nonexhaustive => {
+ try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len +
+ 1 + // owner_union
+ fields_len + // field names
+ ini.values.len); // field values
+
+ const values_map: OptionalMapIndex = if (ini.values.len != 0) m: {
+ const map = try ip.addMap(gpa, ini.values.len);
+ addIndexesToMap(ip, map, ini.values);
+ break :m map.toOptional();
+ } else .none;
+ // We don't clean up the values map on error!
+ errdefer @compileError("error path leaks values_map");
+
+ ip.items.appendAssumeCapacity(.{
+ .tag = switch (ini.tag_mode) {
+ .explicit => .type_enum_explicit,
+ .nonexhaustive => .type_enum_nonexhaustive,
+ .auto => unreachable,
+ },
+ .data = ip.addExtraAssumeCapacity(EnumExplicit{
+ .decl = ini.decl,
+ .captures_len = 0,
+ .namespace = .none,
+ .int_tag_type = ini.tag_ty,
.fields_len = fields_len,
- .zir_index = ini.zir_index,
+ .names_map = names_map,
+ .values_map = values_map,
+ .zir_index = .none,
}),
});
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures));
+ ip.extra.appendAssumeCapacity(@intFromEnum(ini.owner_union_ty));
ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
- return @enumFromInt(ip.items.len - 1);
+ ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.values));
},
- .explicit => return finishGetEnum(ip, gpa, ini, .type_enum_explicit),
- .nonexhaustive => return finishGetEnum(ip, gpa, ini, .type_enum_nonexhaustive),
}
-}
-
-fn finishGetEnum(
- ip: *InternPool,
- gpa: Allocator,
- ini: GetEnumInit,
- tag: Tag,
-) Allocator.Error!Index {
- const names_map = try ip.addMap(gpa, ini.names.len);
- addStringsToMap(ip, names_map, ini.names);
+ // Same as above
+ errdefer @compileError("error path leaks values_map and extra data");
- const values_map: OptionalMapIndex = if (ini.values.len == 0) .none else m: {
- const values_map = try ip.addMap(gpa, ini.values.len);
- addIndexesToMap(ip, values_map, ini.values);
- break :m values_map.toOptional();
- };
- const fields_len: u32 = @intCast(ini.names.len);
- try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len +
- ini.captures.len + fields_len);
- ip.items.appendAssumeCapacity(.{
- .tag = tag,
- .data = ip.addExtraAssumeCapacity(EnumExplicit{
- .decl = ini.decl,
- .captures_len = @intCast(ini.captures.len),
- .namespace = ini.namespace,
- .int_tag_type = ini.tag_ty,
- .fields_len = fields_len,
- .names_map = names_map,
- .values_map = values_map,
- .zir_index = ini.zir_index,
- }),
- });
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures));
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.values));
- return @enumFromInt(ip.items.len - 1);
+ // Capacity for this was ensured earlier
+ const adapter: KeyAdapter = .{ .intern_pool = ip };
+ const gop = ip.map.getOrPutAssumeCapacityAdapted(Key{ .enum_type = .{
+ .generated_tag = .{ .union_type = ini.owner_union_ty },
+ } }, adapter);
+ assert(!gop.found_existing);
+ assert(gop.index == ip.items.len - 1);
+ return @enumFromInt(gop.index);
}
pub const OpaqueTypeIni = struct {
- decl: DeclIndex,
- namespace: NamespaceIndex,
- zir_index: TrackedInst.Index.Optional,
- captures: []const CaptureValue,
+ has_namespace: bool,
+ key: union(enum) {
+ declared: struct {
+ zir_index: TrackedInst.Index,
+ captures: []const CaptureValue,
+ },
+ reified: struct {
+ zir_index: TrackedInst.Index,
+ // No type hash since reifid opaques have no data other than the `@Type` location
+ },
+ },
};
-pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, ini: OpaqueTypeIni) Allocator.Error!Index {
+pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, ini: OpaqueTypeIni) Allocator.Error!WipNamespaceType.Result {
const adapter: KeyAdapter = .{ .intern_pool = ip };
- try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(LoadedOpaqueType).Struct.fields.len + ini.captures.len);
+ const gop = try ip.map.getOrPutAdapted(gpa, Key{ .opaque_type = switch (ini.key) {
+ .declared => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .external = d.captures },
+ } },
+ .reified => |r| .{ .reified = .{
+ .zir_index = r.zir_index,
+ .type_hash = 0,
+ } },
+ } }, adapter);
+ if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) };
+ errdefer _ = ip.map.pop();
try ip.items.ensureUnusedCapacity(gpa, 1);
- const gop = try ip.map.getOrPutAdapted(gpa, Key{
- .opaque_type = .{ .decl = ini.decl },
- }, adapter);
- if (gop.found_existing) return @enumFromInt(gop.index);
+ try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeOpaque).Struct.fields.len + switch (ini.key) {
+ .declared => |d| d.captures.len,
+ .reified => 0,
+ });
+ const extra_index = ip.addExtraAssumeCapacity(Tag.TypeOpaque{
+ .decl = undefined, // set by `finish`
+ .namespace = .none,
+ .zir_index = switch (ini.key) {
+ inline else => |x| x.zir_index,
+ },
+ .captures_len = switch (ini.key) {
+ .declared => |d| @intCast(d.captures.len),
+ .reified => std.math.maxInt(u32),
+ },
+ });
ip.items.appendAssumeCapacity(.{
.tag = .type_opaque,
- .data = ip.addExtraAssumeCapacity(Tag.TypeOpaque{
- .decl = ini.decl,
- .namespace = ini.namespace,
- .zir_index = ini.zir_index,
- .captures_len = @intCast(ini.captures.len),
- }),
+ .data = extra_index,
});
- ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures));
- return @enumFromInt(gop.index);
+ switch (ini.key) {
+ .declared => |d| ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)),
+ .reified => {},
+ }
+ return .{ .wip = .{
+ .index = @enumFromInt(gop.index),
+ .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "decl").?,
+ .namespace_extra_index = if (ini.has_namespace)
+ extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?
+ else
+ null,
+ } };
}
pub fn getIfExists(ip: *const InternPool, key: Key) ?Index {
@@ -6837,8 +7216,34 @@ fn addMap(ip: *InternPool, gpa: Allocator, cap: usize) Allocator.Error!MapIndex
/// This operation only happens under compile error conditions.
/// Leak the index until the next garbage collection.
-/// TODO: this is a bit problematic to implement, can we get away without it?
-pub const remove = @compileError("InternPool.remove is not currently a supported operation; put a TODO there instead");
+/// Invalidates all references to this index.
+pub fn remove(ip: *InternPool, index: Index) void {
+ if (@intFromEnum(index) < static_keys.len) {
+ // The item being removed replaced a special index via `InternPool.resolveBuiltinType`.
+ // Restore the original item at this index.
+ switch (static_keys[@intFromEnum(index)]) {
+ .simple_type => |s| {
+ ip.items.set(@intFromEnum(index), .{
+ .tag = .simple_type,
+ .data = @intFromEnum(s),
+ });
+ },
+ else => unreachable,
+ }
+ return;
+ }
+
+ if (@intFromEnum(index) == ip.items.len - 1) {
+ // Happy case - we can just drop the item without affecting any other indices.
+ ip.items.len -= 1;
+ _ = ip.map.pop();
+ } else {
+ // We must preserve the item so that indices following it remain valid.
+ // Thus, we will rewrite the tag to `removed`, leaking the item until
+ // next GC but causing `KeyAdapter` to ignore it.
+ ip.items.set(@intFromEnum(index), .{ .tag = .removed, .data = undefined });
+ }
+}
fn addInt(ip: *InternPool, gpa: Allocator, ty: Index, tag: Tag, limbs: []const Limb) !void {
const limbs_len = @as(u32, @intCast(limbs.len));
@@ -7635,6 +8040,10 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
if (!gop.found_existing) gop.value_ptr.* = .{};
gop.value_ptr.count += 1;
gop.value_ptr.bytes += 1 + 4 + @as(usize, switch (tag) {
+ // Note that in this case, we have technically leaked some extra data
+ // bytes which we do not account for here.
+ .removed => 0,
+
.type_int_signed => 0,
.type_int_unsigned => 0,
.type_array_small => @sizeOf(Vector),
@@ -7852,6 +8261,8 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {
for (tags, datas, 0..) |tag, data, i| {
try w.print("${d} = {s}(", .{ i, @tagName(tag) });
switch (tag) {
+ .removed => {},
+
.simple_type => try w.print("{s}", .{@tagName(@as(SimpleType, @enumFromInt(data)))}),
.simple_value => try w.print("{s}", .{@tagName(@as(SimpleValue, @enumFromInt(data)))}),
@@ -8258,6 +8669,8 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
// This optimization on tags is needed so that indexToKey can call
// typeOf without being recursive.
_ => switch (ip.items.items(.tag)[@intFromEnum(index)]) {
+ .removed => unreachable,
+
.type_int_signed,
.type_int_unsigned,
.type_array_big,
@@ -8575,6 +8988,8 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois
.var_args_param_type => unreachable, // special tag
_ => switch (ip.items.items(.tag)[@intFromEnum(index)]) {
+ .removed => unreachable,
+
.type_int_signed,
.type_int_unsigned,
=> .Int,
src/Module.zig
@@ -568,9 +568,9 @@ pub const Decl = struct {
.empty_struct_type => .none,
.none => .none,
else => switch (ip.indexToKey(decl.val.toIntern())) {
- .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace.toOptional(),
+ .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace,
.struct_type => ip.loadStructType(decl.val.toIntern()).namespace,
- .union_type => ip.loadUnionType(decl.val.toIntern()).namespace.toOptional(),
+ .union_type => ip.loadUnionType(decl.val.toIntern()).namespace,
.enum_type => ip.loadEnumType(decl.val.toIntern()).namespace,
else => .none,
},
@@ -3302,6 +3302,70 @@ pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void {
return mod.semaFile(file);
}
+fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index {
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+ const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
+ assert(extended.opcode == .struct_decl);
+ const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
+ assert(!small.has_captures_len);
+ assert(!small.has_backing_int);
+ assert(small.layout == .Auto);
+ var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = file.zir.extra[extra_index];
+ extra_index += 1;
+ break :blk fields_len;
+ } else 0;
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = file.zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ const decls = file.zir.bodySlice(extra_index, decls_len);
+ extra_index += decls_len;
+
+ const tracked_inst = try ip.trackZir(gpa, file, .main_struct_inst);
+ const wip_ty = switch (try ip.getStructType(gpa, .{
+ .layout = .Auto,
+ .fields_len = fields_len,
+ .known_non_opv = small.known_non_opv,
+ .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
+ .is_tuple = small.is_tuple,
+ .any_comptime_fields = small.any_comptime_fields,
+ .any_default_inits = small.any_default_inits,
+ .inits_resolved = false,
+ .any_aligned_fields = small.any_aligned_fields,
+ .has_namespace = true,
+ .key = .{ .declared = .{
+ .zir_index = tracked_inst,
+ .captures = &.{},
+ } },
+ })) {
+ .existing => unreachable, // we wouldn't be analysing the file root if this type existed
+ .wip => |wip| wip,
+ };
+ errdefer wip_ty.cancel(ip);
+
+ if (zcu.comp.debug_incremental) {
+ try ip.addDependency(
+ gpa,
+ InternPool.Depender.wrap(.{ .decl = decl_index }),
+ .{ .src_hash = tracked_inst },
+ );
+ }
+
+ const decl = zcu.declPtr(decl_index);
+ decl.val = Value.fromInterned(wip_ty.index);
+ decl.has_tv = true;
+ decl.owns_tv = true;
+ decl.analysis = .complete;
+
+ try zcu.scanNamespace(namespace_index, decls, decl);
+
+ return wip_ty.finish(ip, decl_index, namespace_index.toOptional());
+}
+
/// Regardless of the file status, will create a `Decl` so that we
/// can track dependencies and re-analyze when the file becomes outdated.
pub fn semaFile(mod: *Module, file: *File) SemaError!void {
@@ -3323,7 +3387,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
.decl_index = undefined,
.file_scope = file,
});
- const new_namespace = mod.namespacePtr(new_namespace_index);
errdefer mod.destroyNamespace(new_namespace_index);
const new_decl_index = try mod.allocateNewDecl(new_namespace_index, 0);
@@ -3331,7 +3394,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
errdefer @panic("TODO error handling");
file.root_decl = new_decl_index.toOptional();
- new_namespace.decl_index = new_decl_index;
+ mod.namespacePtr(new_namespace_index).decl_index = new_decl_index;
new_decl.name = try file.fullyQualifiedName(mod);
new_decl.name_fully_qualified = true;
@@ -3350,63 +3413,10 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
}
assert(file.zir_loaded);
- var sema_arena = std.heap.ArenaAllocator.init(gpa);
- defer sema_arena.deinit();
- const sema_arena_allocator = sema_arena.allocator();
-
- var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
- defer comptime_mutable_decls.deinit();
-
- var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa);
- defer comptime_err_ret_trace.deinit();
+ const struct_ty = try mod.getFileRootStruct(new_decl_index, new_namespace_index, file);
+ errdefer mod.intern_pool.remove(struct_ty);
- var sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = sema_arena_allocator,
- .code = file.zir,
- .owner_decl = new_decl,
- .owner_decl_index = new_decl_index,
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = Type.void,
- .fn_ret_ty_ies = null,
- .owner_func_index = .none,
- .comptime_mutable_decls = &comptime_mutable_decls,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
-
- const struct_ty = sema.getStructType(
- new_decl_index,
- new_namespace_index,
- null,
- try mod.intern_pool.trackZir(gpa, file, .main_struct_inst),
- ) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- // The following errors are from resolving capture values, but the root
- // struct of a file has no captures.
- error.AnalysisFail,
- error.NeededSourceLocation,
- error.GenericPoison,
- error.ComptimeReturn,
- error.ComptimeBreak,
- => unreachable,
- };
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer ip.remove(struct_ty);
- for (comptime_mutable_decls.items) |decl_index| {
- const decl = mod.declPtr(decl_index);
- _ = try decl.internValue(mod);
- }
-
- new_decl.val = Value.fromInterned(struct_ty);
- new_decl.has_tv = true;
- new_decl.owns_tv = true;
- new_decl.analysis = .complete;
-
- const comp = mod.comp;
- switch (comp.cache_use) {
+ switch (mod.comp.cache_use) {
.whole => |whole| if (whole.cache_manifest) |man| {
const source = file.getSource(gpa) catch |err| {
try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)});
@@ -5940,14 +5950,6 @@ pub fn atomicPtrAlignment(
return .none;
}
-pub fn opaqueSrcLoc(mod: *Module, opaque_type: InternPool.Key.OpaqueType) SrcLoc {
- return mod.declPtr(opaque_type.decl).srcLoc(mod);
-}
-
-pub fn opaqueFullyQualifiedName(mod: *Module, opaque_type: InternPool.Key.OpaqueType) !InternPool.NullTerminatedString {
- return mod.declPtr(opaque_type.decl).fullyQualifiedName(mod);
-}
-
pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
return mod.declPtr(decl_index).getFileScope(mod);
}
src/Sema.zig
@@ -2685,7 +2685,7 @@ fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_
capture.* = switch (zir_capture.unwrap()) {
.inst => |inst| InternPool.CaptureValue.wrap(capture: {
const air_ref = try sema.resolveInst(inst.toRef());
- if (try sema.resolveValue(air_ref)) |val| {
+ if (try sema.resolveValueResolveLazy(air_ref)) |val| {
break :capture .{ .@"comptime" = val.toIntern() };
}
break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() };
@@ -2697,23 +2697,20 @@ fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_
return captures;
}
-pub fn getStructType(
+fn zirStructDecl(
sema: *Sema,
- decl: InternPool.DeclIndex,
- namespace: InternPool.NamespaceIndex,
- /// The direct parent Namespace for resolving nested capture values.
- parent_namespace: ?InternPool.NamespaceIndex,
- tracked_inst: InternPool.TrackedInst.Index,
-) !InternPool.Index {
+ block: *Block,
+ extended: Zir.Inst.Extended.InstData,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
const mod = sema.mod;
const gpa = sema.gpa;
const ip = &mod.intern_pool;
- const zir_index = tracked_inst.resolve(ip);
- const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended;
- assert(extended.opcode == .struct_decl);
const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
+ const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand);
+ const src = extra.data.src();
+ var extra_index = extra.end;
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
const captures_len = if (small.has_captures_len) blk: {
const captures_len = sema.code.extra[extra_index];
extra_index += 1;
@@ -2730,7 +2727,7 @@ pub fn getStructType(
break :blk decls_len;
} else 0;
- const captures = try sema.getCaptures(parent_namespace, extra_index, captures_len);
+ const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);
extra_index += captures_len;
if (small.has_backing_int) {
@@ -2743,50 +2740,38 @@ pub fn getStructType(
}
}
- const decls = sema.code.bodySlice(extra_index, decls_len);
- try mod.scanNamespace(namespace, decls, mod.declPtr(decl));
- extra_index += decls_len;
-
- const ty = try ip.getStructType(gpa, .{
- .decl = decl,
- .namespace = namespace.toOptional(),
- .zir_index = tracked_inst.toOptional(),
+ const wip_ty = switch (try ip.getStructType(gpa, .{
.layout = small.layout,
- .known_non_opv = small.known_non_opv,
- .is_tuple = small.is_tuple,
.fields_len = fields_len,
+ .known_non_opv = small.known_non_opv,
.requires_comptime = if (small.known_comptime_only) .yes else .unknown,
- .any_default_inits = small.any_default_inits,
+ .is_tuple = small.is_tuple,
.any_comptime_fields = small.any_comptime_fields,
+ .any_default_inits = small.any_default_inits,
.inits_resolved = false,
.any_aligned_fields = small.any_aligned_fields,
- .captures = captures,
- });
-
- return ty;
-}
-
-fn zirStructDecl(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
- const mod = sema.mod;
- const ip = &mod.intern_pool;
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- const src = sema.code.extraData(Zir.Inst.StructDecl, extended.operand).data.src();
-
- // Because these three things each reference each other, `undefined`
- // placeholders are used before being set after the struct type gains an
- // InternPool index.
+ .has_namespace = true or decls_len > 0, // TODO: see below
+ .key = .{ .declared = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ .captures = captures,
+ } },
+ })) {
+ .existing => |ty| return Air.internedToRef(ty),
+ .wip => |wip| wip: {
+ if (sema.builtin_type_target_index == .none) break :wip wip;
+ var new = wip;
+ new.index = sema.builtin_type_target_index;
+ ip.resolveBuiltinType(new.index, wip.index);
+ break :wip new;
+ },
+ };
+ errdefer wip_ty.cancel(ip);
const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
}, small.name_strategy, "struct", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
+ mod.declPtr(new_decl_index).owns_tv = true;
errdefer mod.abortAnonDecl(new_decl_index);
if (sema.mod.comp.debug_incremental) {
@@ -2797,31 +2782,21 @@ fn zirStructDecl(
);
}
- const new_namespace_index = try mod.createNamespace(.{
+ // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace.
+ const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{
.parent = block.namespace.toOptional(),
.decl_index = new_decl_index,
.file_scope = block.getFileScope(mod),
- });
+ })).toOptional() else .none;
errdefer mod.destroyNamespace(new_namespace_index);
- const struct_ty = ty: {
- const tracked_inst = try ip.trackZir(mod.gpa, block.getFileScope(mod), inst);
- const ty = try sema.getStructType(new_decl_index, new_namespace_index, block.namespace, tracked_inst);
- if (sema.builtin_type_target_index != .none) {
- ip.resolveBuiltinType(sema.builtin_type_target_index, ty);
- break :ty sema.builtin_type_target_index;
- }
- break :ty ty;
- };
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer ip.remove(struct_ty);
-
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(struct_ty);
+ if (new_namespace_index.unwrap()) |ns| {
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index));
+ }
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+ return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index));
}
fn createAnonymousDeclTypeNamed(
@@ -2931,6 +2906,7 @@ fn zirEnumDecl(
const mod = sema.mod;
const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand);
var extra_index: usize = extra.end;
@@ -2968,39 +2944,10 @@ fn zirEnumDecl(
break :blk decls_len;
} else 0;
- // Because these three things each reference each other, `undefined`
- // placeholders are used before being set after the enum type gains an
- // InternPool index.
-
- var done = false;
- const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
- }, small.name_strategy, "enum", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
- errdefer if (!done) mod.abortAnonDecl(new_decl_index);
-
- if (sema.mod.comp.debug_incremental) {
- try mod.intern_pool.addDependency(
- sema.gpa,
- InternPool.Depender.wrap(.{ .decl = new_decl_index }),
- .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
- );
- }
-
const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);
extra_index += captures_len;
- const new_namespace_index = try mod.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .decl_index = new_decl_index,
- .file_scope = block.getFileScope(mod),
- });
- errdefer if (!done) mod.destroyNamespace(new_namespace_index);
-
const decls = sema.code.bodySlice(extra_index, decls_len);
- try mod.scanNamespace(new_namespace_index, decls, new_decl);
extra_index += decls_len;
const body = sema.code.bodySlice(extra_index, body_len);
@@ -3014,36 +2961,59 @@ fn zirEnumDecl(
if (bag != 0) break true;
} else false;
- const incomplete_enum = incomplete_enum: {
- var incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{
- .decl = new_decl_index,
- .namespace = new_namespace_index.toOptional(),
- .fields_len = fields_len,
- .has_values = any_values,
- .tag_mode = if (small.nonexhaustive)
- .nonexhaustive
- else if (tag_type_ref == .none)
- .auto
- else
- .explicit,
- .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(),
+ const wip_ty = switch (try ip.getEnumType(gpa, .{
+ .has_namespace = true or decls_len > 0, // TODO: see below
+ .has_values = any_values,
+ .tag_mode = if (small.nonexhaustive)
+ .nonexhaustive
+ else if (tag_type_ref == .none)
+ .auto
+ else
+ .explicit,
+ .fields_len = fields_len,
+ .key = .{ .declared = .{
+ .zir_index = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst),
.captures = captures,
- });
- if (sema.builtin_type_target_index != .none) {
- mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index);
- incomplete_enum.index = sema.builtin_type_target_index;
- }
- break :incomplete_enum incomplete_enum;
+ } },
+ })) {
+ .wip => |wip| wip: {
+ if (sema.builtin_type_target_index == .none) break :wip wip;
+ var new = wip;
+ new.index = sema.builtin_type_target_index;
+ ip.resolveBuiltinType(new.index, wip.index);
+ break :wip new;
+ },
+ .existing => |ty| return Air.internedToRef(ty),
};
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index);
+ errdefer wip_ty.cancel(ip);
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(incomplete_enum.index);
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
+ }, small.name_strategy, "enum", inst);
+ const new_decl = mod.declPtr(new_decl_index);
+ new_decl.owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
- const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index);
- try mod.finalizeAnonDecl(new_decl_index);
- done = true;
+ if (sema.mod.comp.debug_incremental) {
+ try mod.intern_pool.addDependency(
+ sema.gpa,
+ InternPool.Depender.wrap(.{ .decl = new_decl_index }),
+ .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
+ );
+ }
+
+ // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace.
+ const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{
+ .parent = block.namespace.toOptional(),
+ .decl_index = new_decl_index,
+ .file_scope = block.getFileScope(mod),
+ })).toOptional() else .none;
+ errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns);
+
+ if (new_namespace_index.unwrap()) |ns| {
+ try mod.scanNamespace(ns, decls, new_decl);
+ }
const int_tag_ty = ty: {
// We create a block for the field type instructions because they
@@ -3072,7 +3042,7 @@ fn zirEnumDecl(
.parent = null,
.sema = sema,
.src_decl = new_decl_index,
- .namespace = new_namespace_index,
+ .namespace = new_namespace_index.unwrap() orelse block.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = true,
@@ -3088,7 +3058,6 @@ fn zirEnumDecl(
if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) {
return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)});
}
- incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern());
break :ty ty;
} else if (fields_len == 0) {
break :ty try mod.intType(.unsigned, 0);
@@ -3098,6 +3067,8 @@ fn zirEnumDecl(
}
};
+ wip_ty.prepare(ip, new_decl_index, new_namespace_index, int_tag_ty.toIntern());
+
if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) {
return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
@@ -3121,7 +3092,6 @@ fn zirEnumDecl(
extra_index += 2; // field name, doc comment
const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir);
- assert(incomplete_enum.addFieldName(&mod.intern_pool, field_name) == null);
const tag_overflow = if (has_tag_value) overflow: {
const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
@@ -3142,12 +3112,13 @@ fn zirEnumDecl(
};
if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
- if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| {
+ if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| {
+ assert(conflict.kind == .value); // AstGen validated names are unique
const value_src = mod.fieldSrcLoc(new_decl_index, .{
.index = field_i,
.range = .value,
}).lazy;
- const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
+ const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = conflict.prev_field_idx }).lazy;
const msg = msg: {
const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
errdefer msg.destroy(gpa);
@@ -3164,9 +3135,10 @@ fn zirEnumDecl(
else
try mod.intValue(int_tag_ty, 0);
if (overflow != null) break :overflow true;
- if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| {
+ if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| {
+ assert(conflict.kind == .value); // AstGen validated names are unique
const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy;
- const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy;
+ const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = conflict.prev_field_idx }).lazy;
const msg = msg: {
const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)});
errdefer msg.destroy(gpa);
@@ -3177,6 +3149,7 @@ fn zirEnumDecl(
}
break :overflow false;
} else overflow: {
+ assert(wip_ty.nextField(&mod.intern_pool, field_name, .none) == null);
last_tag_val = try mod.intValue(Type.comptime_int, field_i);
if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty);
@@ -3194,7 +3167,9 @@ fn zirEnumDecl(
return sema.failWithOwnedErrorMsg(block, msg);
}
}
- return decl_val;
+
+ try mod.finalizeAnonDecl(new_decl_index);
+ return Air.internedToRef(wip_ty.finish(ip));
}
fn zirUnionDecl(
@@ -3208,6 +3183,7 @@ fn zirUnionDecl(
const mod = sema.mod;
const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand);
var extra_index: usize = extra.end;
@@ -3233,16 +3209,53 @@ fn zirUnionDecl(
break :blk decls_len;
} else 0;
- // Because these three things each reference each other, `undefined`
- // placeholders are used before being set after the union type gains an
- // InternPool index.
+ const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);
+ extra_index += captures_len;
+
+ const wip_ty = switch (try ip.getUnionType(gpa, .{
+ .flags = .{
+ .layout = small.layout,
+ .status = .none,
+ .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
+ .tagged
+ else if (small.layout != .Auto)
+ .none
+ else switch (block.wantSafety()) {
+ true => .safety,
+ false => .none,
+ },
+ .any_aligned_fields = small.any_aligned_fields,
+ .requires_comptime = .unknown,
+ .assumed_runtime_bits = false,
+ .assumed_pointer_aligned = false,
+ .alignment = .none,
+ },
+ .has_namespace = true or decls_len != 0, // TODO: see below
+ .fields_len = fields_len,
+ .enum_tag_ty = .none, // set later
+ .field_types = &.{}, // set later
+ .field_aligns = &.{}, // set later
+ .key = .{ .declared = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ .captures = captures,
+ } },
+ })) {
+ .wip => |wip| wip: {
+ if (sema.builtin_type_target_index == .none) break :wip wip;
+ var new = wip;
+ new.index = sema.builtin_type_target_index;
+ ip.resolveBuiltinType(new.index, wip.index);
+ break :wip new;
+ },
+ .existing => |ty| return Air.internedToRef(ty),
+ };
+ errdefer wip_ty.cancel(ip);
const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
}, small.name_strategy, "union", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
+ mod.declPtr(new_decl_index).owns_tv = true;
errdefer mod.abortAnonDecl(new_decl_index);
if (sema.mod.comp.debug_incremental) {
@@ -3253,62 +3266,22 @@ fn zirUnionDecl(
);
}
- const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);
- extra_index += captures_len;
-
- const new_namespace_index = try mod.createNamespace(.{
+ // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace.
+ const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{
.parent = block.namespace.toOptional(),
.decl_index = new_decl_index,
.file_scope = block.getFileScope(mod),
- });
- errdefer mod.destroyNamespace(new_namespace_index);
-
- const union_ty = ty: {
- const ty = try mod.intern_pool.getUnionType(gpa, .{
- .flags = .{
- .layout = small.layout,
- .status = .none,
- .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
- .tagged
- else if (small.layout != .Auto)
- .none
- else switch (block.wantSafety()) {
- true => .safety,
- false => .none,
- },
- .any_aligned_fields = small.any_aligned_fields,
- .requires_comptime = .unknown,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
- .alignment = .none,
- },
- .decl = new_decl_index,
- .namespace = new_namespace_index,
- .zir_index = (try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst)).toOptional(),
- .fields_len = fields_len,
- .enum_tag_ty = .none,
- .field_types = &.{},
- .field_aligns = &.{},
- .captures = captures,
- });
- if (sema.builtin_type_target_index != .none) {
- mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty);
- break :ty sema.builtin_type_target_index;
- }
- break :ty ty;
- };
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer mod.intern_pool.remove(union_ty);
+ })).toOptional() else .none;
+ errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns);
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(union_ty);
-
- const decls = sema.code.bodySlice(extra_index, decls_len);
- try mod.scanNamespace(new_namespace_index, decls, new_decl);
+ if (new_namespace_index.unwrap()) |ns| {
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index));
+ }
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+
+ return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index));
}
fn zirOpaqueDecl(
@@ -3321,6 +3294,9 @@ fn zirOpaqueDecl(
defer tracy.end();
const mod = sema.mod;
+ const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
+
const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand);
var extra_index: usize = extra.end;
@@ -3339,54 +3315,51 @@ fn zirOpaqueDecl(
break :blk decls_len;
} else 0;
- // Because these three things each reference each other, `undefined`
- // placeholders are used in two places before being set after the opaque
- // type gains an InternPool index.
+ const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);
+ extra_index += captures_len;
+
+ const wip_ty = switch (try ip.getOpaqueType(gpa, .{
+ .has_namespace = decls_len != 0,
+ .key = .{ .declared = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ .captures = captures,
+ } },
+ })) {
+ .wip => |wip| wip,
+ .existing => |ty| return Air.internedToRef(ty),
+ };
+ errdefer wip_ty.cancel(ip);
const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
}, small.name_strategy, "opaque", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
+ mod.declPtr(new_decl_index).owns_tv = true;
errdefer mod.abortAnonDecl(new_decl_index);
if (sema.mod.comp.debug_incremental) {
- try mod.intern_pool.addDependency(
- sema.gpa,
+ try ip.addDependency(
+ gpa,
InternPool.Depender.wrap(.{ .decl = new_decl_index }),
- .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) },
+ .{ .src_hash = try ip.trackZir(gpa, block.getFileScope(mod), inst) },
);
}
- const captures = try sema.getCaptures(block.namespace, extra_index, captures_len);
- extra_index += captures_len;
-
- const new_namespace_index = try mod.createNamespace(.{
+ const new_namespace_index: InternPool.OptionalNamespaceIndex = if (decls_len > 0) (try mod.createNamespace(.{
.parent = block.namespace.toOptional(),
.decl_index = new_decl_index,
.file_scope = block.getFileScope(mod),
- });
- errdefer mod.destroyNamespace(new_namespace_index);
-
- const opaque_ty = try mod.intern_pool.getOpaqueType(sema.gpa, .{
- .decl = new_decl_index,
- .namespace = new_namespace_index,
- .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(),
- .captures = captures,
- });
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer mod.intern_pool.remove(opaque_ty);
-
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(opaque_ty);
+ })).toOptional() else .none;
+ errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns);
- const decls = sema.code.bodySlice(extra_index, decls_len);
- try mod.scanNamespace(new_namespace_index, decls, new_decl);
+ if (new_namespace_index.unwrap()) |ns| {
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index));
+ }
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+
+ return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index));
}
fn zirErrorSetDecl(
@@ -21322,103 +21295,11 @@ fn zirReify(
try ip.getOrPutString(gpa, "is_exhaustive"),
).?);
- // Decls
if (decls_val.sliceLen(mod) > 0) {
return sema.fail(block, src, "reified enums must have no decls", .{});
}
- const int_tag_ty = tag_type_val.toType();
- if (int_tag_ty.zigTypeTag(mod) != .Int) {
- return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
- }
-
- // Because these things each reference each other, `undefined`
- // placeholders are used before being set after the enum type gains
- // an InternPool index.
-
- const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
- }, name_strategy, "enum", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
- errdefer {
- new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
- mod.abortAnonDecl(new_decl_index);
- }
-
- // Define our empty enum decl
- const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)));
- const incomplete_enum = try ip.getIncompleteEnum(gpa, .{
- .decl = new_decl_index,
- .namespace = .none,
- .fields_len = fields_len,
- .has_values = true,
- .tag_mode = if (!is_exhaustive_val.toBool())
- .nonexhaustive
- else
- .explicit,
- .tag_ty = int_tag_ty.toIntern(),
- .zir_index = .none,
- .captures = &.{},
- });
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer ip.remove(incomplete_enum.index);
-
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(incomplete_enum.index);
-
- for (0..fields_len) |field_i| {
- const elem_val = try fields_val.elemValue(mod, field_i);
- const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
- const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "name"),
- ).?);
- const value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "value"),
- ).?);
-
- const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
-
- if (!try sema.intFitsInType(value_val, int_tag_ty, null)) {
- // TODO: better source location
- return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{
- field_name.fmt(ip),
- value_val.fmtValue(Type.comptime_int, mod),
- int_tag_ty.fmt(mod),
- });
- }
-
- if (incomplete_enum.addFieldName(ip, field_name)) |other_index| {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{
- field_name.fmt(ip),
- });
- errdefer msg.destroy(gpa);
- _ = other_index; // TODO: this note is incorrect
- try sema.errNote(block, src, msg, "other field here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
-
- if (incomplete_enum.addFieldValue(ip, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)});
- errdefer msg.destroy(gpa);
- _ = other; // TODO: this note is incorrect
- try sema.errNote(block, src, msg, "other enum tag value here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- }
-
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
- try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+ return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_val, name_strategy);
},
.Opaque => {
const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
@@ -21432,43 +21313,27 @@ fn zirReify(
return sema.fail(block, src, "reified opaque must have no decls", .{});
}
- // Because these three things each reference each other,
- // `undefined` placeholders are used in two places before being set
- // after the opaque type gains an InternPool index.
+ const wip_ty = switch (try ip.getOpaqueType(gpa, .{
+ .has_namespace = false,
+ .key = .{ .reified = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ } },
+ })) {
+ .existing => |ty| return Air.internedToRef(ty),
+ .wip => |wip| wip,
+ };
+ errdefer wip_ty.cancel(ip);
const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
}, name_strategy, "opaque", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
- errdefer {
- new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
- mod.abortAnonDecl(new_decl_index);
- }
-
- const new_namespace_index = try mod.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .decl_index = new_decl_index,
- .file_scope = block.getFileScope(mod),
- });
- errdefer mod.destroyNamespace(new_namespace_index);
-
- const opaque_ty = try ip.getOpaqueType(gpa, .{
- .decl = new_decl_index,
- .namespace = new_namespace_index,
- .zir_index = .none,
- .captures = &.{},
- });
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer ip.remove(opaque_ty);
-
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(opaque_ty);
+ mod.declPtr(new_decl_index).owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+
+ return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none));
},
.Union => {
const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
@@ -21489,214 +21354,12 @@ fn zirReify(
try ip.getOrPutString(gpa, "decls"),
).?);
- // Decls
if (decls_val.sliceLen(mod) > 0) {
return sema.fail(block, src, "reified unions must have no decls", .{});
}
const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val);
- const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)));
-
- // Tag type
- var explicit_tags_seen: []bool = &.{};
- var enum_field_names: []InternPool.NullTerminatedString = &.{};
- var enum_tag_ty: InternPool.Index = .none;
- if (tag_type_val.optionalValue(mod)) |payload_val| {
- enum_tag_ty = payload_val.toType().toIntern();
-
- const enum_type = switch (ip.indexToKey(enum_tag_ty)) {
- .enum_type => ip.loadEnumType(enum_tag_ty),
- else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
- };
-
- explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
- @memset(explicit_tags_seen, false);
- } else {
- enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
- }
-
- // Fields
- var any_aligned_fields: bool = false;
- var union_fields: std.MultiArrayList(struct {
- type: InternPool.Index,
- alignment: InternPool.Alignment,
- }) = .{};
- var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
- try field_name_table.ensureTotalCapacity(sema.arena, fields_len);
- for (0..fields_len) |i| {
- const elem_val = try fields_val.elemValue(mod, i);
- const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
- const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "name"),
- ).?);
- const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "type"),
- ).?);
- const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "alignment"),
- ).?);
-
- const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
-
- if (enum_field_names.len != 0) {
- enum_field_names[i] = field_name;
- }
-
- if (enum_tag_ty != .none) {
- const tag_info = ip.loadEnumType(enum_tag_ty);
- const enum_index = tag_info.nameIndex(ip, field_name) orelse {
- return sema.fail(block, src, "no field named '{}' in enum '{}'", .{
- field_name.fmt(ip), Type.fromInterned(enum_tag_ty).fmt(mod),
- });
- };
- assert(explicit_tags_seen.len == tag_info.names.len);
- // No check for duplicate because the check already happened in order
- // to create the enum type in the first place.
- assert(!explicit_tags_seen[enum_index]);
- explicit_tags_seen[enum_index] = true;
- }
-
- const gop = field_name_table.getOrPutAssumeCapacity(field_name);
- if (gop.found_existing) {
- // TODO: better source location
- return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)});
- }
-
- const field_ty = type_val.toType();
- const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
- if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) {
- // TODO: better source location
- return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{
- alignment_val_int,
- });
- }
- const field_align = Alignment.fromByteUnits(alignment_val_int);
- any_aligned_fields = any_aligned_fields or field_align != .none;
-
- try union_fields.append(sema.arena, .{
- .type = field_ty.toIntern(),
- .alignment = field_align,
- });
-
- if (field_ty.zigTypeTag(mod) == .Opaque) {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
- errdefer msg.destroy(gpa);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
- errdefer msg.destroy(gpa);
-
- const src_decl = mod.declPtr(block.src_decl);
- try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .union_field);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
- errdefer msg.destroy(gpa);
-
- const src_decl = mod.declPtr(block.src_decl);
- try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty);
-
- try sema.addDeclaredHereNote(msg, field_ty);
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- }
-
- if (enum_tag_ty != .none) {
- const tag_info = ip.loadEnumType(enum_tag_ty);
- if (tag_info.names.len > fields_len) {
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
- errdefer msg.destroy(gpa);
-
- assert(explicit_tags_seen.len == tag_info.names.len);
- for (tag_info.names.get(ip), 0..) |field_name, field_index| {
- if (explicit_tags_seen[field_index]) continue;
- try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{
- field_name.fmt(ip),
- });
- }
- try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty));
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- } else {
- enum_tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, .none);
- }
-
- // Because these three things each reference each other, `undefined`
- // placeholders are used before being set after the union type gains an
- // InternPool index.
-
- const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
- }, name_strategy, "union", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
- errdefer {
- new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
- mod.abortAnonDecl(new_decl_index);
- }
-
- const new_namespace_index = try mod.createNamespace(.{
- .parent = block.namespace.toOptional(),
- .decl_index = new_decl_index,
- .file_scope = block.getFileScope(mod),
- });
- errdefer mod.destroyNamespace(new_namespace_index);
-
- const union_ty = try ip.getUnionType(gpa, .{
- .decl = new_decl_index,
- .namespace = new_namespace_index,
- .enum_tag_ty = enum_tag_ty,
- .fields_len = fields_len,
- .zir_index = .none,
- .flags = .{
- .layout = layout,
- .status = .have_field_types,
- .runtime_tag = if (!tag_type_val.isNull(mod))
- .tagged
- else if (layout != .Auto)
- .none
- else switch (block.wantSafety()) {
- true => .safety,
- false => .none,
- },
- .any_aligned_fields = any_aligned_fields,
- .requires_comptime = .unknown,
- .assumed_runtime_bits = false,
- .assumed_pointer_aligned = false,
- .alignment = .none,
- },
- .field_types = union_fields.items(.type),
- .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{},
- .captures = &.{},
- });
-
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(union_ty);
-
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
- try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+ return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_val, name_strategy);
},
.Fn => {
const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
@@ -21795,127 +21458,491 @@ fn zirReify(
}
}
-fn reifyStruct(
+fn reifyEnum(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
src: LazySrcLoc,
- layout: std.builtin.Type.ContainerLayout,
- backing_int_val: Value,
+ tag_ty: Type,
+ is_exhaustive: bool,
fields_val: Value,
name_strategy: Zir.Inst.NameStrategy,
- is_tuple: bool,
) CompileError!Air.Inst.Ref {
const mod = sema.mod;
const gpa = sema.gpa;
const ip = &mod.intern_pool;
- if (is_tuple) switch (layout) {
- .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}),
- .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}),
- .Auto => {},
+ // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`.
+
+ const fields_len: u32 = @intCast(fields_val.sliceLen(mod));
+
+ // The validation work here is non-trivial, and it's possible the type already exists.
+ // So in this first pass, let's just construct a hash to optimize for this case. If the
+ // inputs turn out to be invalid, we can cancel the WIP type later.
+
+ // For deduplication purposes, we must create a hash including all details of this type.
+ // TODO: use a longer hash!
+ var hasher = std.hash.Wyhash.init(0);
+ std.hash.autoHash(&hasher, tag_ty.toIntern());
+ std.hash.autoHash(&hasher, is_exhaustive);
+ std.hash.autoHash(&hasher, fields_len);
+
+ for (0..fields_len) |field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
+
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1));
+
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
+
+ std.hash.autoHash(&hasher, .{
+ field_name,
+ field_value_val.toIntern(),
+ });
+ }
+
+ const wip_ty = switch (try ip.getEnumType(gpa, .{
+ .has_namespace = false,
+ .has_values = true,
+ .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive,
+ .fields_len = fields_len,
+ .key = .{ .reified = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ .type_hash = hasher.final(),
+ } },
+ })) {
+ .wip => |wip| wip,
+ .existing => |ty| return Air.internedToRef(ty),
};
+ errdefer wip_ty.cancel(ip);
+
+ if (tag_ty.zigTypeTag(mod) != .Int) {
+ return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
+ }
+
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
+ }, name_strategy, "enum", inst);
+ mod.declPtr(new_decl_index).owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+
+ wip_ty.prepare(ip, new_decl_index, .none, tag_ty.toIntern());
+
+ for (0..fields_len) |field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
- const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod)));
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1));
- // Because these three things each reference each other, `undefined`
- // placeholders are used before being set after the struct type gains an
- // InternPool index.
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
+
+ if (!try sema.intFitsInType(field_value_val, tag_ty, null)) {
+ // TODO: better source location
+ return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{
+ field_name.fmt(ip),
+ field_value_val.fmtValue(Type.comptime_int, mod),
+ tag_ty.fmt(mod),
+ });
+ }
+
+ const coerced_field_val = try mod.getCoerced(field_value_val, tag_ty);
+ if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| {
+ return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) {
+ .name => msg: {
+ const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{field_name.fmt(ip)});
+ errdefer msg.destroy(gpa);
+ _ = conflict.prev_field_idx; // TODO: this note is incorrect
+ try sema.errNote(block, src, msg, "other field here", .{});
+ break :msg msg;
+ },
+ .value => msg: {
+ const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{field_value_val.fmtValue(Type.comptime_int, mod)});
+ errdefer msg.destroy(gpa);
+ _ = conflict.prev_field_idx; // TODO: this note is incorrect
+ try sema.errNote(block, src, msg, "other enum tag value here", .{});
+ break :msg msg;
+ },
+ });
+ }
+ }
+
+ if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(mod)) {
+ return sema.fail(block, src, "non-exhaustive enum specified every value", .{});
+ }
+
+ try mod.finalizeAnonDecl(new_decl_index);
+ return Air.internedToRef(wip_ty.finish(ip));
+}
+
+fn reifyUnion(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ src: LazySrcLoc,
+ layout: std.builtin.Type.ContainerLayout,
+ opt_tag_type_val: Value,
+ fields_val: Value,
+ name_strategy: Zir.Inst.NameStrategy,
+) CompileError!Air.Inst.Ref {
+ const mod = sema.mod;
+ const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
+
+ // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`.
+
+ const fields_len: u32 = @intCast(fields_val.sliceLen(mod));
+
+ // The validation work here is non-trivial, and it's possible the type already exists.
+ // So in this first pass, let's just construct a hash to optimize for this case. If the
+ // inputs turn out to be invalid, we can cancel the WIP type later.
+
+ // For deduplication purposes, we must create a hash including all details of this type.
+ // TODO: use a longer hash!
+ var hasher = std.hash.Wyhash.init(0);
+ std.hash.autoHash(&hasher, layout);
+ std.hash.autoHash(&hasher, opt_tag_type_val.toIntern());
+ std.hash.autoHash(&hasher, fields_len);
+
+ var any_aligns = false;
+
+ for (0..fields_len) |field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
+
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_type_val = try field_info.fieldValue(mod, 1);
+ const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 2));
+
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
+
+ std.hash.autoHash(&hasher, .{
+ field_name,
+ field_type_val.toIntern(),
+ field_align_val.toIntern(),
+ });
+
+ if (field_align_val.toUnsignedInt(mod) != 0) {
+ any_aligns = true;
+ }
+ }
+
+ const wip_ty = switch (try ip.getUnionType(gpa, .{
+ .flags = .{
+ .layout = layout,
+ .status = .none,
+ .runtime_tag = if (opt_tag_type_val.optionalValue(mod) != null)
+ .tagged
+ else if (layout != .Auto)
+ .none
+ else switch (block.wantSafety()) {
+ true => .safety,
+ false => .none,
+ },
+ .any_aligned_fields = any_aligns,
+ .requires_comptime = .unknown,
+ .assumed_runtime_bits = false,
+ .assumed_pointer_aligned = false,
+ .alignment = .none,
+ },
+ .has_namespace = false,
+ .fields_len = fields_len,
+ .enum_tag_ty = .none, // set later because not yet validated
+ .field_types = &.{}, // set later
+ .field_aligns = &.{}, // set later
+ .key = .{ .reified = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ .type_hash = hasher.final(),
+ } },
+ })) {
+ .wip => |wip| wip,
+ .existing => |ty| return Air.internedToRef(ty),
+ };
+ errdefer wip_ty.cancel(ip);
const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
- }, name_strategy, "struct", inst);
- const new_decl = mod.declPtr(new_decl_index);
- new_decl.owns_tv = true;
- errdefer {
- new_decl.has_tv = false; // namespace and val were destroyed by later errdefers
- mod.abortAnonDecl(new_decl_index);
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
+ }, name_strategy, "union", inst);
+ mod.declPtr(new_decl_index).owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+
+ const field_types = try sema.arena.alloc(InternPool.Index, fields_len);
+ const field_aligns = if (any_aligns) try sema.arena.alloc(InternPool.Alignment, fields_len) else undefined;
+
+ const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(mod)) |tag_type_val| tag_ty: {
+ switch (ip.indexToKey(tag_type_val.toIntern())) {
+ .enum_type => {},
+ else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
+ }
+ const enum_tag_ty = tag_type_val.toType();
+
+ // We simply track which fields of the tag type have been seen.
+ const tag_ty_fields_len = enum_tag_ty.enumFieldCount(mod);
+ var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len);
+
+ for (field_types, 0..) |*field_ty, field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
+
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_type_val = try field_info.fieldValue(mod, 1);
+
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
+
+ const enum_index = enum_tag_ty.enumFieldIndex(field_name, mod) orelse {
+ // TODO: better source location
+ return sema.fail(block, src, "no field named '{}' in enum '{}'", .{
+ field_name.fmt(ip), enum_tag_ty.fmt(mod),
+ });
+ };
+ if (seen_tags.isSet(enum_index)) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)});
+ }
+ seen_tags.set(enum_index);
+
+ field_ty.* = field_type_val.toIntern();
+ if (any_aligns) {
+ const byte_align = try (try field_info.fieldValue(mod, 2)).toUnsignedIntAdvanced(sema);
+ if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) {
+ // TODO: better source location
+ return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align});
+ }
+ field_aligns[field_idx] = Alignment.fromByteUnits(byte_align);
+ }
+ }
+
+ if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(block, src, "enum fields missing in union", .{});
+ errdefer msg.destroy(gpa);
+ var it = seen_tags.iterator(.{ .kind = .unset });
+ while (it.next()) |enum_index| {
+ const field_name = enum_tag_ty.enumFieldName(enum_index, mod);
+ try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{}' missing, declared here", .{
+ field_name.fmt(ip),
+ });
+ }
+ try sema.addDeclaredHereNote(msg, enum_tag_ty);
+ break :msg msg;
+ });
+
+ break :tag_ty .{ enum_tag_ty.toIntern(), true };
+ } else tag_ty: {
+ // We must track field names and set up the tag type ourselves.
+ var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
+ try field_names.ensureTotalCapacity(sema.arena, fields_len);
+
+ for (field_types, 0..) |*field_ty, field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
+
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_type_val = try field_info.fieldValue(mod, 1);
+
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
+ const gop = field_names.getOrPutAssumeCapacity(field_name);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)});
+ }
+
+ field_ty.* = field_type_val.toIntern();
+ if (any_aligns) {
+ const byte_align = try (try field_info.fieldValue(mod, 2)).toUnsignedIntAdvanced(sema);
+ if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) {
+ // TODO: better source location
+ return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align});
+ }
+ field_aligns[field_idx] = Alignment.fromByteUnits(byte_align);
+ }
+ }
+
+ const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), mod.declPtr(new_decl_index));
+ break :tag_ty .{ enum_tag_ty, false };
+ };
+ errdefer if (!has_explicit_tag) ip.remove(enum_tag_ty); // remove generated tag type on error
+
+ for (field_types) |field_ty_ip| {
+ const field_ty = Type.fromInterned(field_ty_ip);
+ if (field_ty.zigTypeTag(mod) == .Opaque) {
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
+ errdefer msg.destroy(gpa);
+
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
+ if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
+ errdefer msg.destroy(gpa);
+
+ const src_decl = mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field);
+
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) {
+ return sema.failWithOwnedErrorMsg(block, msg: {
+ const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
+ errdefer msg.destroy(gpa);
+
+ const src_decl = mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
+
+ try sema.addDeclaredHereNote(msg, field_ty);
+ break :msg msg;
+ });
+ }
}
- const ty = try ip.getStructType(gpa, .{
- .decl = new_decl_index,
- .namespace = .none,
- .zir_index = .none,
+ const loaded_union = ip.loadUnionType(wip_ty.index);
+ loaded_union.setFieldTypes(ip, field_types);
+ if (any_aligns) {
+ loaded_union.setFieldAligns(ip, field_aligns);
+ }
+ loaded_union.tagTypePtr(ip).* = enum_tag_ty;
+ loaded_union.flagsPtr(ip).status = .have_field_types;
+
+ try mod.finalizeAnonDecl(new_decl_index);
+ return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none));
+}
+
+fn reifyStruct(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ src: LazySrcLoc,
+ layout: std.builtin.Type.ContainerLayout,
+ opt_backing_int_val: Value,
+ fields_val: Value,
+ name_strategy: Zir.Inst.NameStrategy,
+ is_tuple: bool,
+) CompileError!Air.Inst.Ref {
+ const mod = sema.mod;
+ const gpa = sema.gpa;
+ const ip = &mod.intern_pool;
+
+ // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`.
+
+ const fields_len: u32 = @intCast(fields_val.sliceLen(mod));
+
+ // The validation work here is non-trivial, and it's possible the type already exists.
+ // So in this first pass, let's just construct a hash to optimize for this case. If the
+ // inputs turn out to be invalid, we can cancel the WIP type later.
+
+ // For deduplication purposes, we must create a hash including all details of this type.
+ // TODO: use a longer hash!
+ var hasher = std.hash.Wyhash.init(0);
+ std.hash.autoHash(&hasher, layout);
+ std.hash.autoHash(&hasher, opt_backing_int_val.toIntern());
+ std.hash.autoHash(&hasher, is_tuple);
+ std.hash.autoHash(&hasher, fields_len);
+
+ var any_comptime_fields = false;
+ var any_default_inits = false;
+ var any_aligned_fields = false;
+
+ for (0..fields_len) |field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
+
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_type_val = try field_info.fieldValue(mod, 1);
+ const field_default_value_val = try field_info.fieldValue(mod, 2);
+ const field_is_comptime_val = try field_info.fieldValue(mod, 3);
+ const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 4));
+
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
+ const field_is_comptime = field_is_comptime_val.toBool();
+ const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(mod)) |ptr_val| d: {
+ const ptr_ty = try mod.singleConstPtrType(field_type_val.toType());
+ // We need to do this deref here, so we won't check for this error case later on.
+ const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime(
+ block,
+ src,
+ .{ .needed_comptime_reason = "struct field default value must be comptime-known" },
+ );
+ // Resolve the value so that lazy values do not create distinct types.
+ break :d (try sema.resolveLazyValue(val)).toIntern();
+ } else .none;
+
+ std.hash.autoHash(&hasher, .{
+ field_name,
+ field_type_val.toIntern(),
+ field_default_value,
+ field_is_comptime,
+ field_alignment_val.toIntern(),
+ });
+
+ if (field_is_comptime) any_comptime_fields = true;
+ if (field_default_value != .none) any_default_inits = true;
+ switch (try field_alignment_val.orderAgainstZeroAdvanced(mod, sema)) {
+ .eq => {},
+ .gt => any_aligned_fields = true,
+ .lt => unreachable,
+ }
+ }
+
+ const wip_ty = switch (try ip.getStructType(gpa, .{
.layout = layout,
- .known_non_opv = false,
.fields_len = fields_len,
+ .known_non_opv = false,
.requires_comptime = .unknown,
.is_tuple = is_tuple,
- // So that we don't have to scan ahead, we allocate space in the struct
- // type for alignments, comptime fields, and default inits. This might
- // result in wasted space, however, this is a permitted encoding of
- // struct types.
- .any_comptime_fields = true,
- .any_default_inits = true,
+ .any_comptime_fields = any_comptime_fields,
+ .any_default_inits = any_default_inits,
+ .any_aligned_fields = any_aligned_fields,
.inits_resolved = true,
- .any_aligned_fields = true,
- .captures = &.{},
- });
- // TODO: figure out InternPool removals for incremental compilation
- //errdefer ip.remove(ty);
- const struct_type = ip.loadStructType(ty);
+ .has_namespace = false,
+ .key = .{ .reified = .{
+ .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst),
+ .type_hash = hasher.final(),
+ } },
+ })) {
+ .wip => |wip| wip,
+ .existing => |ty| return Air.internedToRef(ty),
+ };
+ errdefer wip_ty.cancel(ip);
- new_decl.ty = Type.type;
- new_decl.val = Value.fromInterned(ty);
-
- // Fields
- for (0..fields_len) |i| {
- const elem_val = try fields_val.elemValue(mod, i);
- const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
- const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "name"),
- ).?);
- const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "type"),
- ).?);
- const default_value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "default_value"),
- ).?);
- const is_comptime_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "is_comptime"),
- ).?);
- const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex(
- ip,
- try ip.getOrPutString(gpa, "alignment"),
- ).?);
-
- if (!try sema.intFitsInType(alignment_val, Type.u32, null)) {
- return sema.fail(block, src, "alignment must fit in 'u32'", .{});
- }
- const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?;
-
- if (layout == .Packed) {
- if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
- if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
- } else {
- if (abi_align > 0 and !math.isPowerOfTwo(abi_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{abi_align});
- struct_type.field_aligns.get(ip)[i] = Alignment.fromByteUnits(abi_align);
- }
- if (layout == .Extern and is_comptime_val.toBool()) {
- return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
- }
+ if (is_tuple) switch (layout) {
+ .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}),
+ .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}),
+ .Auto => {},
+ };
+
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
+ .ty = Type.type,
+ .val = Value.fromInterned(wip_ty.index),
+ }, name_strategy, "struct", inst);
+ mod.declPtr(new_decl_index).owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+
+ const struct_type = ip.loadStructType(wip_ty.index);
+
+ for (0..fields_len) |field_idx| {
+ const field_info = try fields_val.elemValue(mod, field_idx);
- const field_name = try name_val.toIpString(Type.slice_const_u8, mod);
+ const field_name_val = try field_info.fieldValue(mod, 0);
+ const field_type_val = try field_info.fieldValue(mod, 1);
+ const field_default_value_val = try field_info.fieldValue(mod, 2);
+ const field_is_comptime_val = try field_info.fieldValue(mod, 3);
+ const field_alignment_val = try field_info.fieldValue(mod, 4);
+ const field_ty = field_type_val.toType();
+ const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod);
if (is_tuple) {
- const field_index = field_name.toUnsigned(ip) orelse return sema.fail(
+ const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail(
block,
src,
"tuple cannot have non-numeric field '{}'",
.{field_name.fmt(ip)},
);
-
- if (field_index >= fields_len) {
+ if (field_name_index != field_idx) {
return sema.fail(
block,
src,
- "tuple field {} exceeds tuple field count",
- .{field_index},
+ "tuple field name '{}' does not match field index {}",
+ .{ field_name_index, field_idx },
);
}
} else if (struct_type.addFieldName(ip, field_name)) |prev_index| {
@@ -21923,45 +21950,72 @@ fn reifyStruct(
return sema.fail(block, src, "duplicate struct field name {}", .{field_name.fmt(ip)});
}
- const field_ty = type_val.toType();
- const default_val = if (default_value_val.optionalValue(mod)) |opt_val|
- (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse
- return sema.failWithNeededComptime(block, src, .{
- .needed_comptime_reason = "struct field default value must be comptime-known",
- })).toIntern()
- else
- .none;
- if (is_comptime_val.toBool() and default_val == .none) {
+ if (any_aligned_fields) {
+ if (!try sema.intFitsInType(field_alignment_val, Type.u32, null)) {
+ return sema.fail(block, src, "alignment must fit in 'u32'", .{});
+ }
+
+ const byte_align = try field_alignment_val.toUnsignedIntAdvanced(sema);
+ if (byte_align == 0) {
+ if (layout != .Packed) {
+ struct_type.field_aligns.get(ip)[field_idx] = .none;
+ }
+ } else {
+ if (layout == .Packed) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
+ if (!math.isPowerOfTwo(byte_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align});
+ struct_type.field_aligns.get(ip)[field_idx] = Alignment.fromNonzeroByteUnits(byte_align);
+ }
+ }
+
+ const field_is_comptime = field_is_comptime_val.toBool();
+ if (field_is_comptime) {
+ assert(any_comptime_fields);
+ switch (layout) {
+ .Extern => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}),
+ .Packed => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}),
+ .Auto => struct_type.setFieldComptime(ip, field_idx),
+ }
+ }
+
+ const field_default: InternPool.Index = d: {
+ if (!any_default_inits) break :d .none;
+ const ptr_val = field_default_value_val.optionalValue(mod) orelse break :d .none;
+ const ptr_ty = try mod.singleConstPtrType(field_ty);
+ // Asserted comptime-dereferencable above.
+ const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?;
+ // We already resolved this for deduplication, so we may as well do it now.
+ break :d (try sema.resolveLazyValue(val)).toIntern();
+ };
+
+ if (field_is_comptime and field_default == .none) {
return sema.fail(block, src, "comptime field without default initialization value", .{});
}
- struct_type.field_types.get(ip)[i] = field_ty.toIntern();
- struct_type.field_inits.get(ip)[i] = default_val;
- if (is_comptime_val.toBool())
- struct_type.setFieldComptime(ip, i);
+ struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern();
+ if (field_default != .none) {
+ struct_type.field_inits.get(ip)[field_idx] = field_default;
+ }
if (field_ty.zigTypeTag(mod) == .Opaque) {
- const msg = msg: {
+ return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
errdefer msg.destroy(gpa);
try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
+ });
}
if (field_ty.zigTypeTag(mod) == .NoReturn) {
- const msg = msg: {
+ return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{});
errdefer msg.destroy(gpa);
try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
+ });
}
if (layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) {
- const msg = msg: {
+ return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
errdefer msg.destroy(gpa);
@@ -21970,10 +22024,9 @@ fn reifyStruct(
try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
+ });
} else if (layout == .Packed and !try sema.validatePackedType(field_ty)) {
- const msg = msg: {
+ return sema.failWithOwnedErrorMsg(block, msg: {
const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
errdefer msg.destroy(gpa);
@@ -21982,32 +22035,27 @@ fn reifyStruct(
try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
+ });
}
}
if (layout == .Packed) {
- for (0..struct_type.field_types.len) |index| {
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]);
+ var fields_bit_sum: u64 = 0;
+ for (0..struct_type.field_types.len) |field_idx| {
+ const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_idx]);
sema.resolveTypeLayout(field_ty) catch |err| switch (err) {
error.AnalysisFail => {
const msg = sema.err orelse return err;
- try sema.addFieldErrNote(Type.fromInterned(ty), index, msg, "while checking this field", .{});
+ try sema.errNote(block, src, msg, "while checking a field of this struct", .{});
return err;
},
else => return err,
};
- }
-
- var fields_bit_sum: u64 = 0;
- for (0..struct_type.field_types.len) |i| {
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
fields_bit_sum += field_ty.bitSize(mod);
}
- if (backing_int_val.optionalValue(mod)) |backing_int_ty_val| {
- const backing_int_ty = backing_int_ty_val.toType();
+ if (opt_backing_int_val.optionalValue(mod)) |backing_int_val| {
+ const backing_int_ty = backing_int_val.toType();
try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
struct_type.backingIntType(ip).* = backing_int_ty.toIntern();
} else {
@@ -22016,9 +22064,8 @@ fn reifyStruct(
}
}
- const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
- return decl_val;
+ return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none));
}
fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
@@ -36963,8 +37010,8 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded
const gpa = mod.gpa;
const ip = &mod.intern_pool;
const decl_index = union_type.decl;
- const zir = mod.namespacePtr(union_type.namespace).file_scope.zir;
- const zir_index = union_type.zir_index.unwrap().?.resolve(ip);
+ const zir = mod.namespacePtr(union_type.namespace.unwrap().?).file_scope.zir;
+ const zir_index = union_type.zir_index.resolve(ip);
const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
assert(extended.opcode == .union_decl);
const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
@@ -37037,7 +37084,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded
.parent = null,
.sema = &sema,
.src_decl = decl_index,
- .namespace = union_type.namespace,
+ .namespace = union_type.namespace.unwrap().?,
.instructions = .{},
.inlining = null,
.is_comptime = true,
@@ -37357,7 +37404,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded
const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), mod.declPtr(union_type.decl));
union_type.tagTypePtr(ip).* = enum_ty;
} else {
- const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_type.decl.toOptional());
+ const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, mod.declPtr(union_type.decl));
union_type.tagTypePtr(ip).* = enum_ty;
}
}
@@ -37374,7 +37421,7 @@ fn generateUnionTagTypeNumbered(
block: *Block,
enum_field_names: []const InternPool.NullTerminatedString,
enum_field_vals: []const InternPool.Index,
- decl: *Module.Decl,
+ union_owner_decl: *Module.Decl,
) !InternPool.Index {
const mod = sema.mod;
const gpa = sema.gpa;
@@ -37383,7 +37430,7 @@ fn generateUnionTagTypeNumbered(
const src_decl = mod.declPtr(block.src_decl);
const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node);
errdefer mod.destroyDecl(new_decl_index);
- const fqn = try decl.fullyQualifiedName(mod);
+ const fqn = try union_owner_decl.fullyQualifiedName(mod);
const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)});
try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{
.ty = Type.noreturn,
@@ -37395,9 +37442,9 @@ fn generateUnionTagTypeNumbered(
new_decl.owns_tv = true;
new_decl.name_fully_qualified = true;
- const enum_ty = try ip.getEnum(gpa, .{
+ const enum_ty = try ip.getGeneratedTagEnumType(gpa, .{
.decl = new_decl_index,
- .namespace = .none,
+ .owner_union_ty = union_owner_decl.val.toIntern(),
.tag_ty = if (enum_field_vals.len == 0)
(try mod.intType(.unsigned, 0)).toIntern()
else
@@ -37405,8 +37452,6 @@ fn generateUnionTagTypeNumbered(
.names = enum_field_names,
.values = enum_field_vals,
.tag_mode = .explicit,
- .zir_index = .none,
- .captures = &.{},
});
new_decl.ty = Type.type;
@@ -37420,20 +37465,14 @@ fn generateUnionTagTypeSimple(
sema: *Sema,
block: *Block,
enum_field_names: []const InternPool.NullTerminatedString,
- maybe_decl_index: InternPool.OptionalDeclIndex,
+ union_owner_decl: *Module.Decl,
) !InternPool.Index {
const mod = sema.mod;
const ip = &mod.intern_pool;
const gpa = sema.gpa;
const new_decl_index = new_decl_index: {
- const decl_index = maybe_decl_index.unwrap() orelse {
- break :new_decl_index try mod.createAnonymousDecl(block, .{
- .ty = Type.noreturn,
- .val = Value.@"unreachable",
- });
- };
- const fqn = try mod.declPtr(decl_index).fullyQualifiedName(mod);
+ const fqn = try union_owner_decl.fullyQualifiedName(mod);
const src_decl = mod.declPtr(block.src_decl);
const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node);
errdefer mod.destroyDecl(new_decl_index);
@@ -37447,9 +37486,9 @@ fn generateUnionTagTypeSimple(
};
errdefer mod.abortAnonDecl(new_decl_index);
- const enum_ty = try ip.getEnum(gpa, .{
+ const enum_ty = try ip.getGeneratedTagEnumType(gpa, .{
.decl = new_decl_index,
- .namespace = .none,
+ .owner_union_ty = union_owner_decl.val.toIntern(),
.tag_ty = if (enum_field_names.len == 0)
(try mod.intType(.unsigned, 0)).toIntern()
else
@@ -37457,8 +37496,6 @@ fn generateUnionTagTypeSimple(
.names = enum_field_names,
.values = &.{},
.tag_mode = .auto,
- .zir_index = .none,
- .captures = &.{},
});
const new_decl = mod.declPtr(new_decl_index);
@@ -37643,6 +37680,8 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
=> unreachable,
_ => switch (ip.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
+ .removed => unreachable,
+
.type_int_signed, // i0 handled above
.type_int_unsigned, // u0 handled above
.type_pointer,
src/type.zig
@@ -355,16 +355,16 @@ pub const Type = struct {
try writer.writeAll("}");
},
- .union_type => |union_type| {
- const decl = mod.declPtr(union_type.decl);
+ .union_type => {
+ const decl = mod.declPtr(ip.loadUnionType(ty.toIntern()).decl);
try decl.renderFullyQualifiedName(mod, writer);
},
- .opaque_type => |opaque_type| {
- const decl = mod.declPtr(opaque_type.decl);
+ .opaque_type => {
+ const decl = mod.declPtr(ip.loadOpaqueType(ty.toIntern()).decl);
try decl.renderFullyQualifiedName(mod, writer);
},
- .enum_type => |enum_type| {
- const decl = mod.declPtr(enum_type.decl);
+ .enum_type => {
+ const decl = mod.declPtr(ip.loadEnumType(ty.toIntern()).decl);
try decl.renderFullyQualifiedName(mod, writer);
},
.func_type => |fn_info| {
@@ -2845,9 +2845,9 @@ pub const Type = struct {
pub fn getNamespaceIndex(ty: Type, mod: *Module) InternPool.OptionalNamespaceIndex {
const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
- .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace.toOptional(),
+ .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace,
.struct_type => ip.loadStructType(ty.toIntern()).namespace,
- .union_type => ip.loadUnionType(ty.toIntern()).namespace.toOptional(),
+ .union_type => ip.loadUnionType(ty.toIntern()).namespace,
.enum_type => ip.loadEnumType(ty.toIntern()).namespace,
else => .none,
@@ -3180,17 +3180,8 @@ pub const Type = struct {
}
pub fn declSrcLocOrNull(ty: Type, mod: *Module) ?Module.SrcLoc {
- return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
- .struct_type => |struct_type| {
- return mod.declPtr(struct_type.decl.unwrap() orelse return null).srcLoc(mod);
- },
- .union_type => |union_type| {
- return mod.declPtr(union_type.decl).srcLoc(mod);
- },
- .opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type),
- .enum_type => |enum_type| mod.declPtr(enum_type.decl).srcLoc(mod),
- else => null,
- };
+ const decl = ty.getOwnerDeclOrNull(mod) orelse return null;
+ return mod.declPtr(decl).srcLoc(mod);
}
pub fn getOwnerDecl(ty: Type, mod: *Module) InternPool.DeclIndex {
@@ -3198,11 +3189,12 @@ pub const Type = struct {
}
pub fn getOwnerDeclOrNull(ty: Type, mod: *Module) ?InternPool.DeclIndex {
- return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
- .struct_type => |struct_type| struct_type.decl.unwrap(),
- .union_type => |union_type| union_type.decl,
- .opaque_type => |opaque_type| opaque_type.decl,
- .enum_type => |enum_type| enum_type.decl,
+ const ip = &mod.intern_pool;
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .struct_type => ip.loadStructType(ty.toIntern()).decl.unwrap(),
+ .union_type => ip.loadUnionType(ty.toIntern()).decl,
+ .opaque_type => ip.loadOpaqueType(ty.toIntern()).decl,
+ .enum_type => ip.loadEnumType(ty.toIntern()).decl,
else => null,
};
}
@@ -3287,9 +3279,9 @@ pub const Type = struct {
const ip = &zcu.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
.struct_type => ip.loadStructType(ty.toIntern()).zir_index.unwrap(),
- .union_type => ip.loadUnionType(ty.toIntern()).zir_index.unwrap(),
+ .union_type => ip.loadUnionType(ty.toIntern()).zir_index,
.enum_type => ip.loadEnumType(ty.toIntern()).zir_index.unwrap(),
- .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index.unwrap(),
+ .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index,
else => null,
};
}