diff --git a/src/InternPool.zig b/src/InternPool.zig index 323e64f0e1..86dd29c90a 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -501,6 +501,42 @@ pub const OptionalNullTerminatedString = enum(u32) { } }; +/// A single value captured in the closure of a namespace type. This is not a plain +/// `Index` because we must differentiate between runtime-known values (where we +/// store the type) and comptime-known values (where we store the value). +pub const CaptureValue = packed struct(u32) { + tag: enum { @"comptime", runtime }, + idx: u31, + + pub fn wrap(val: Unwrapped) CaptureValue { + return switch (val) { + .@"comptime" => |i| .{ .tag = .@"comptime", .idx = @intCast(@intFromEnum(i)) }, + .runtime => |i| .{ .tag = .runtime, .idx = @intCast(@intFromEnum(i)) }, + }; + } + pub fn unwrap(val: CaptureValue) Unwrapped { + return switch (val.tag) { + .@"comptime" => .{ .@"comptime" = @enumFromInt(val.idx) }, + .runtime => .{ .runtime = @enumFromInt(val.idx) }, + }; + } + + pub const Unwrapped = union(enum) { + /// Index refers to the value. + @"comptime": Index, + /// Index refers to the type. + runtime: Index, + }; + + pub const Slice = struct { + start: u32, + len: u32, + pub fn get(slice: Slice, ip: *const InternPool) []CaptureValue { + return @ptrCast(ip.extra.items[slice.start..][0..slice.len]); + } + }; +}; + pub const Key = union(enum) { int_type: IntType, ptr_type: PtrType, @@ -707,6 +743,7 @@ pub const Key = union(enum) { /// 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"); @@ -1660,6 +1697,7 @@ pub const LoadedUnionType = struct { field_aligns: Alignment.Slice, /// Index of the union_decl ZIR instruction. zir_index: TrackedInst.Index.Optional, + captures: CaptureValue.Slice, pub const RuntimeTag = enum(u2) { none, @@ -1791,24 +1829,47 @@ pub const LoadedUnionType = struct { }; pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { - const extra_index = ip.items.items(.data)[@intFromEnum(index)]; - const type_union = ip.extraDataTrail(Tag.TypeUnion, extra_index); + const data = ip.items.items(.data)[@intFromEnum(index)]; + const type_union = ip.extraDataTrail(Tag.TypeUnion, data); const fields_len = type_union.data.fields_len; + var extra_index = type_union.end; + const captures_len = if (type_union.data.flags.any_captures) c: { + const len = ip.extra.items[extra_index]; + extra_index += 1; + break :c len; + } else 0; + + const captures: CaptureValue.Slice = .{ + .start = extra_index, + .len = captures_len, + }; + extra_index += captures_len; + + const field_types: Index.Slice = .{ + .start = extra_index, + .len = fields_len, + }; + extra_index += fields_len; + + const field_aligns: Alignment.Slice = if (type_union.data.flags.any_aligned_fields) a: { + const a: Alignment.Slice = .{ + .start = extra_index, + .len = fields_len, + }; + extra_index += std.math.divCeil(u32, fields_len, 4) catch unreachable; + break :a a; + } else .{ .start = 0, .len = 0 }; + return .{ - .extra_index = extra_index, + .extra_index = data, .decl = type_union.data.decl, .namespace = type_union.data.namespace, .enum_tag_ty = type_union.data.tag_ty, - .field_types = .{ - .start = type_union.end, - .len = fields_len, - }, - .field_aligns = .{ - .start = type_union.end + fields_len, - .len = if (type_union.data.flags.any_aligned_fields) fields_len else 0, - }, + .field_types = field_types, + .field_aligns = field_aligns, .zir_index = type_union.data.zir_index, + .captures = captures, }; } @@ -1830,6 +1891,7 @@ pub const LoadedStructType = struct { comptime_bits: ComptimeBits, offsets: Offsets, names_map: OptionalMapIndex, + captures: CaptureValue.Slice, pub const ComptimeBits = struct { start: u32, @@ -2162,10 +2224,26 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .comptime_bits = .{ .start = 0, .len = 0 }, .offsets = .{ .start = 0, .len = 0 }, .names_map = .none, + .captures = .{ .start = 0, .len = 0 }, }; const extra = ip.extraDataTrail(Tag.TypeStruct, item.data); const fields_len = extra.data.fields_len; - var extra_index = extra.end + fields_len; // skip field types + var extra_index = extra.end; + const captures_len = if (extra.data.flags.any_captures) c: { + const len = ip.extra.items[extra_index]; + extra_index += 1; + break :c len; + } else 0; + const captures: CaptureValue.Slice = .{ + .start = extra_index, + .len = captures_len, + }; + extra_index += captures_len; + const field_types: Index.Slice = .{ + .start = extra_index, + .len = fields_len, + }; + extra_index += fields_len; const names_map: OptionalMapIndex, const names: NullTerminatedString.Slice = if (!extra.data.flags.is_tuple) n: { const names_map: OptionalMapIndex = @enumFromInt(ip.extra.items[extra_index]); extra_index += 1; @@ -2211,42 +2289,64 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .zir_index = extra.data.zir_index, .layout = if (extra.data.flags.is_extern) .Extern else .Auto, .field_names = names, - .field_types = .{ .start = extra.end, .len = fields_len }, + .field_types = field_types, .field_inits = inits, .field_aligns = aligns, .runtime_order = runtime_order, .comptime_bits = comptime_bits, .offsets = offsets, .names_map = names_map, + .captures = captures, }; }, .type_struct_packed, .type_struct_packed_inits => { const extra = ip.extraDataTrail(Tag.TypeStructPacked, item.data); const has_inits = item.tag == .type_struct_packed_inits; const fields_len = extra.data.fields_len; + var extra_index = extra.end; + const captures_len = if (extra.data.flags.any_captures) c: { + const len = ip.extra.items[extra_index]; + extra_index += 1; + break :c len; + } else 0; + const captures: CaptureValue.Slice = .{ + .start = extra_index, + .len = captures_len, + }; + extra_index += captures_len; + const field_types: Index.Slice = .{ + .start = extra_index, + .len = fields_len, + }; + extra_index += fields_len; + const field_names: NullTerminatedString.Slice = .{ + .start = extra_index, + .len = fields_len, + }; + extra_index += fields_len; + const field_inits: Index.Slice = if (has_inits) inits: { + const i: Index.Slice = .{ + .start = extra_index, + .len = fields_len, + }; + extra_index += fields_len; + break :inits i; + } else .{ .start = 0, .len = 0 }; return .{ .extra_index = item.data, .decl = extra.data.decl.toOptional(), .namespace = extra.data.namespace, .zir_index = extra.data.zir_index, .layout = .Packed, - .field_names = .{ - .start = extra.end + fields_len, - .len = fields_len, - }, - .field_types = .{ - .start = extra.end, - .len = fields_len, - }, - .field_inits = if (has_inits) .{ - .start = extra.end + 2 * fields_len, - .len = fields_len, - } else .{ .start = 0, .len = 0 }, + .field_names = field_names, + .field_types = field_types, + .field_inits = field_inits, .field_aligns = .{ .start = 0, .len = 0 }, .runtime_order = .{ .start = 0, .len = 0 }, .comptime_bits = .{ .start = 0, .len = 0 }, .offsets = .{ .start = 0, .len = 0 }, .names_map = extra.data.names_map.toOptional(), + .captures = captures, }; }, else => unreachable, @@ -2273,6 +2373,7 @@ const LoadedEnumType = struct { /// This is guaranteed to not be `.none` if explicit values are provided. values_map: OptionalMapIndex, zir_index: TrackedInst.Index.Optional, + captures: CaptureValue.Slice, pub const TagMode = enum { /// The integer tag type was auto-numbered by zig. @@ -2332,7 +2433,7 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .namespace = extra.data.namespace, .tag_ty = extra.data.int_tag_type, .names = .{ - .start = @intCast(extra.end), + .start = @intCast(extra.end + extra.data.captures_len), .len = extra.data.fields_len, }, .values = .{ .start = 0, .len = 0 }, @@ -2340,6 +2441,10 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .names_map = extra.data.names_map, .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 => { @@ -2349,11 +2454,11 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .namespace = extra.data.namespace, .tag_ty = extra.data.int_tag_type, .names = .{ - .start = @intCast(extra.end), + .start = @intCast(extra.end + extra.data.captures_len), .len = extra.data.fields_len, }, .values = .{ - .start = @intCast(extra.end + extra.data.fields_len), + .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) { @@ -2364,6 +2469,10 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .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, + }, }; }, else => unreachable, @@ -2378,12 +2487,22 @@ pub const LoadedOpaqueType = struct { namespace: NamespaceIndex, /// The index of the `opaque_decl` instruction. zir_index: TrackedInst.Index.Optional, + captures: CaptureValue.Slice, }; 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)]; - return ip.extraData(LoadedOpaqueType, extra_index); + const extra = ip.extraDataTrail(Tag.TypeOpaque, extra_index); + return .{ + .decl = extra.data.decl, + .namespace = extra.data.namespace, + .zir_index = extra.data.zir_index, + .captures = .{ + .start = extra.end, + .len = extra.data.captures_len, + }, + }; } pub const Item = struct { @@ -2601,7 +2720,7 @@ pub const Index = enum(u32) { type_enum_explicit: DataIsExtraIndexOfEnumExplicit, type_enum_nonexhaustive: DataIsExtraIndexOfEnumExplicit, simple_type: struct { data: SimpleType }, - type_opaque: struct { data: *Key.OpaqueType }, + type_opaque: struct { data: *Tag.TypeOpaque }, type_struct: struct { data: *Tag.TypeStruct }, type_struct_anon: DataIsExtraIndexOfTypeStructAnon, type_struct_packed: struct { data: *Tag.TypeStructPacked }, @@ -3036,7 +3155,7 @@ pub const Tag = enum(u8) { /// data is SimpleType enum value. simple_type, /// An opaque type. - /// data is index of Key.OpaqueType in extra. + /// data is index of Tag.TypeOpaque in extra. type_opaque, /// A non-packed struct type. /// data is 0 or extra index of `TypeStruct`. @@ -3239,7 +3358,6 @@ pub const Tag = enum(u8) { memoized_call, const ErrorUnionType = Key.ErrorUnionType; - const OpaqueType = LoadedOpaqueType; const TypeValue = Key.TypeValue; const Error = Key.Error; const EnumTag = Key.EnumTag; @@ -3266,7 +3384,7 @@ pub const Tag = enum(u8) { .type_enum_explicit => EnumExplicit, .type_enum_nonexhaustive => EnumExplicit, .simple_type => unreachable, - .type_opaque => OpaqueType, + .type_opaque => TypeOpaque, .type_struct => TypeStruct, .type_struct_anon => TypeStructAnon, .type_struct_packed, .type_struct_packed_inits => TypeStructPacked, @@ -3424,8 +3542,10 @@ pub const Tag = enum(u8) { }; /// Trailing: - /// 0. field type: Index for each field; declaration order - /// 1. field align: Alignment for each field; declaration order + /// 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 pub const TypeUnion = struct { flags: Flags, /// This could be provided through the tag type, but it is more convenient @@ -3443,6 +3563,7 @@ pub const Tag = enum(u8) { zir_index: TrackedInst.Index.Optional, pub const Flags = packed struct(u32) { + any_captures: bool, runtime_tag: LoadedUnionType.RuntimeTag, /// If false, the field alignment trailing data is omitted. any_aligned_fields: bool, @@ -3452,14 +3573,16 @@ pub const Tag = enum(u8) { assumed_runtime_bits: bool, assumed_pointer_aligned: bool, alignment: Alignment, - _: u14 = 0, + _: u13 = 0, }; }; /// Trailing: - /// 0. type: Index for each fields_len - /// 1. name: NullTerminatedString for each fields_len - /// 2. init: Index for each fields_len // if tag is type_struct_packed_inits + /// 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 pub const TypeStructPacked = struct { decl: DeclIndex, zir_index: TrackedInst.Index.Optional, @@ -3470,10 +3593,11 @@ pub const Tag = enum(u8) { flags: Flags, pub const Flags = packed struct(u32) { + any_captures: bool, /// Dependency loop detection when resolving field inits. field_inits_wip: bool, inits_resolved: bool, - _: u30 = 0, + _: u29 = 0, }; }; @@ -3492,21 +3616,23 @@ pub const Tag = enum(u8) { /// than coming up with some other scheme for the data. /// /// Trailing: - /// 0. type: Index for each field in declared order - /// 1. if not is_tuple: + /// 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: /// names_map: MapIndex, /// name: NullTerminatedString // for each field in declared order - /// 2. if any_default_inits: + /// 4. if any_default_inits: /// init: Index // for each field in declared order - /// 3. if has_namespace: + /// 5. if has_namespace: /// namespace: NamespaceIndex - /// 4. if any_aligned_fields: + /// 6. if any_aligned_fields: /// align: Alignment // for each field in declared order - /// 5. if any_comptime_fields: + /// 7. if any_comptime_fields: /// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 0 - /// 6. if not is_extern: + /// 8. if not is_extern: /// field_index: RuntimeOrder // for each field in runtime order - /// 7. field_offset: u32 // for each field in declared order, undef until layout_resolved + /// 9. field_offset: u32 // for each field in declared order, undef until layout_resolved pub const TypeStruct = struct { decl: DeclIndex, zir_index: TrackedInst.Index.Optional, @@ -3515,6 +3641,7 @@ pub const Tag = enum(u8) { size: u32, pub const Flags = packed struct(u32) { + any_captures: bool, is_extern: bool, known_non_opv: bool, requires_comptime: RequiresComptime, @@ -3544,9 +3671,21 @@ pub const Tag = enum(u8) { // which `layout_resolved` does not ensure. fully_resolved: bool, - _: u8 = 0, + _: u7 = 0, }; }; + + /// Trailing: + /// 0. capture: CaptureValue // for each `captures_len` + pub const TypeOpaque = 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, + captures_len: u32, + }; }; /// State that is mutable during semantic analysis. This data is not used for @@ -3853,11 +3992,13 @@ pub const Array = struct { }; /// Trailing: -/// 0. field name: NullTerminatedString for each fields_len; declaration order -/// 1. tag value: Index for each fields_len; declaration order +/// 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 pub const EnumExplicit = struct { /// The Decl that corresponds to the enum itself. decl: DeclIndex, + captures_len: u32, /// This may be `none` if there are no declarations. namespace: OptionalNamespaceIndex, /// An integer type which is used for the numerical value of the enum, which @@ -3874,10 +4015,12 @@ pub const EnumExplicit = struct { }; /// Trailing: -/// 0. field name: NullTerminatedString for each fields_len; declaration order +/// 0. capture: CaptureValue // for each `captures_len` +/// 1. field name: NullTerminatedString for each fields_len; declaration order pub const EnumAuto = struct { /// The Decl that corresponds to the enum itself. decl: DeclIndex, + captures_len: u32, /// This may be `none` if there are no declarations. namespace: OptionalNamespaceIndex, /// An integer type which is used for the numerical value of the enum, which @@ -4187,7 +4330,9 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .inferred_error_set_type = @enumFromInt(data), }, - .type_opaque => .{ .opaque_type = ip.extraData(Key.OpaqueType, data) }, + .type_opaque => .{ .opaque_type = .{ + .decl = ip.extraData(Tag.TypeOpaque, data).decl, + } }, .type_struct => .{ .struct_type = if (data == 0) .{ .decl = .none, @@ -5497,7 +5642,16 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { } pub const UnionTypeInit = struct { - flags: Tag.TypeUnion.Flags, + flags: packed struct { + runtime_tag: LoadedUnionType.RuntimeTag, + any_aligned_fields: bool, + layout: std.builtin.Type.ContainerLayout, + status: LoadedUnionType.Status, + requires_comptime: RequiresComptime, + assumed_runtime_bits: bool, + assumed_pointer_aligned: bool, + alignment: Alignment, + }, decl: DeclIndex, namespace: NamespaceIndex, zir_index: TrackedInst.Index.Optional, @@ -5509,6 +5663,7 @@ 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, }; pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocator.Error!Index { @@ -5516,12 +5671,24 @@ pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocat 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 ini.fields_len + // field types align_elements_len); try ip.items.ensureUnusedCapacity(gpa, 1); const union_type_extra_index = ip.addExtraAssumeCapacity(Tag.TypeUnion{ - .flags = ini.flags, + .flags = .{ + .any_captures = ini.captures.len != 0, + .runtime_tag = ini.flags.runtime_tag, + .any_aligned_fields = ini.flags.any_aligned_fields, + .layout = ini.flags.layout, + .status = ini.flags.status, + .requires_comptime = ini.flags.requires_comptime, + .assumed_runtime_bits = ini.flags.assumed_runtime_bits, + .assumed_pointer_aligned = ini.flags.assumed_pointer_aligned, + .alignment = ini.flags.alignment, + }, .fields_len = ini.fields_len, .size = std.math.maxInt(u32), .padding = std.math.maxInt(u32), @@ -5531,6 +5698,11 @@ pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocat .zir_index = ini.zir_index, }); + if (ini.captures.len != 0) { + ip.extra.appendAssumeCapacity(@intCast(ini.captures.len)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + } + // field types if (ini.field_types.len > 0) { assert(ini.field_types.len == ini.fields_len); @@ -5582,6 +5754,7 @@ pub const StructTypeInit = struct { any_default_inits: bool, inits_resolved: bool, any_aligned_fields: bool, + captures: []const CaptureValue, }; pub fn getStructType( @@ -5605,6 +5778,8 @@ pub fn getStructType( .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 ini.fields_len + // types ini.fields_len + // names ini.fields_len); // inits @@ -5618,11 +5793,16 @@ pub fn getStructType( .backing_int_ty = .none, .names_map = names_map, .flags = .{ + .any_captures = ini.captures.len != 0, .field_inits_wip = false, .inits_resolved = ini.inits_resolved, }, }), }); + if (ini.captures.len != 0) { + ip.extra.appendAssumeCapacity(@intCast(ini.captures.len)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + } ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len); if (ini.any_default_inits) { @@ -5637,6 +5817,8 @@ 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 (ini.fields_len * 5) + // types, names, inits, runtime order, offsets align_elements_len + comptime_elements_len + 2); // names_map + namespace @@ -5648,6 +5830,7 @@ pub fn getStructType( .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, @@ -5669,6 +5852,10 @@ pub fn getStructType( }, }), }); + if (ini.captures.len != 0) { + ip.extra.appendAssumeCapacity(@intCast(ini.captures.len)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + } ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); if (!ini.is_tuple) { ip.extra.appendAssumeCapacity(@intFromEnum(names_map)); @@ -6405,11 +6592,12 @@ fn getIncompleteEnumAuto( 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.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, @@ -6421,6 +6609,7 @@ fn getIncompleteEnumAuto( .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), @@ -6455,11 +6644,12 @@ fn getIncompleteEnumExplicit( if (enum_type.has_values) enum_type.fields_len else 0; const extra_fields_len: u32 = @typeInfo(EnumExplicit).Struct.fields.len; - try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + reserved_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, @@ -6472,6 +6662,7 @@ fn getIncompleteEnumExplicit( .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 .{ @@ -6492,6 +6683,7 @@ pub const GetEnumInit = struct { 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 { @@ -6513,11 +6705,12 @@ pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Erro const fields_len: u32 = @intCast(ini.names.len); try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len + - fields_len); + ini.captures.len + fields_len); ip.items.appendAssumeCapacity(.{ .tag = .type_enum_auto, .data = ip.addExtraAssumeCapacity(EnumAuto{ .decl = ini.decl, + .captures_len = @intCast(ini.captures.len), .namespace = ini.namespace, .int_tag_type = ini.tag_ty, .names_map = names_map, @@ -6525,6 +6718,7 @@ pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Erro .zir_index = ini.zir_index, }), }); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names)); return @enumFromInt(ip.items.len - 1); }, @@ -6533,7 +6727,7 @@ pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Erro } } -pub fn finishGetEnum( +fn finishGetEnum( ip: *InternPool, gpa: Allocator, ini: GetEnumInit, @@ -6549,11 +6743,12 @@ pub fn finishGetEnum( }; const fields_len: u32 = @intCast(ini.names.len); try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len + - 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, @@ -6562,23 +6757,37 @@ pub fn finishGetEnum( .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); } -pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, key: LoadedOpaqueType) Allocator.Error!Index { +pub const OpaqueTypeIni = struct { + decl: DeclIndex, + namespace: NamespaceIndex, + zir_index: TrackedInst.Index.Optional, + captures: []const CaptureValue, +}; + +pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, ini: OpaqueTypeIni) Allocator.Error!Index { const adapter: KeyAdapter = .{ .intern_pool = ip }; - try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(LoadedOpaqueType).Struct.fields.len); + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(LoadedOpaqueType).Struct.fields.len + ini.captures.len); try ip.items.ensureUnusedCapacity(gpa, 1); const gop = try ip.map.getOrPutAdapted(gpa, Key{ - .opaque_type = .{ .decl = key.decl }, + .opaque_type = .{ .decl = ini.decl }, }, adapter); if (gop.found_existing) return @enumFromInt(gop.index); ip.items.appendAssumeCapacity(.{ .tag = .type_opaque, - .data = ip.addExtraAssumeCapacity(key), + .data = ip.addExtraAssumeCapacity(Tag.TypeOpaque{ + .decl = ini.decl, + .namespace = ini.namespace, + .zir_index = ini.zir_index, + .captures_len = @intCast(ini.captures.len), + }), }); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); return @enumFromInt(gop.index); } @@ -7442,12 +7651,31 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { break :b @sizeOf(Tag.ErrorSet) + (@sizeOf(u32) * info.names_len); }, .type_inferred_error_set => 0, - .type_enum_explicit, .type_enum_nonexhaustive => @sizeOf(EnumExplicit), - .type_enum_auto => @sizeOf(EnumAuto), - .type_opaque => @sizeOf(Key.OpaqueType), + .type_enum_explicit, .type_enum_nonexhaustive => b: { + const info = ip.extraData(EnumExplicit, data); + var ints = @typeInfo(EnumExplicit).Struct.fields.len + info.captures_len + info.fields_len; + if (info.values_map != .none) ints += info.fields_len; + break :b @sizeOf(u32) * ints; + }, + .type_enum_auto => b: { + const info = ip.extraData(EnumAuto, data); + const ints = @typeInfo(EnumAuto).Struct.fields.len + info.captures_len + info.fields_len; + break :b @sizeOf(u32) * ints; + }, + .type_opaque => b: { + const info = ip.extraData(Tag.TypeOpaque, data); + const ints = @typeInfo(Tag.TypeOpaque).Struct.fields.len + info.captures_len; + break :b @sizeOf(u32) * ints; + }, .type_struct => b: { - const info = ip.extraData(Tag.TypeStruct, data); + if (data == 0) break :b 0; + const extra = ip.extraDataTrail(Tag.TypeStruct, data); + const info = extra.data; var ints: usize = @typeInfo(Tag.TypeStruct).Struct.fields.len; + if (info.flags.any_captures) { + const captures_len = ip.extra.items[extra.end]; + ints += 1 + captures_len; + } ints += info.fields_len; // types if (!info.flags.is_tuple) { ints += 1; // names_map @@ -7470,14 +7698,24 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { break :b @sizeOf(TypeStructAnon) + (@sizeOf(u32) * 3 * info.fields_len); }, .type_struct_packed => b: { - const info = ip.extraData(Tag.TypeStructPacked, data); + const extra = ip.extraDataTrail(Tag.TypeStructPacked, data); + const captures_len = if (extra.data.flags.any_captures) + ip.extra.items[extra.end] + else + 0; break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).Struct.fields.len + - info.fields_len + info.fields_len); + @intFromBool(extra.data.flags.any_captures) + captures_len + + extra.data.fields_len * 2); }, .type_struct_packed_inits => b: { - const info = ip.extraData(Tag.TypeStructPacked, data); + const extra = ip.extraDataTrail(Tag.TypeStructPacked, data); + const captures_len = if (extra.data.flags.any_captures) + ip.extra.items[extra.end] + else + 0; break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).Struct.fields.len + - info.fields_len + info.fields_len + info.fields_len); + @intFromBool(extra.data.flags.any_captures) + captures_len + + extra.data.fields_len * 3); }, .type_tuple_anon => b: { const info = ip.extraData(TypeStructAnon, data); @@ -7485,16 +7723,20 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { }, .type_union => b: { - const info = ip.extraData(Tag.TypeUnion, data); - const enum_info = ip.loadEnumType(info.tag_ty); - const fields_len: u32 = @intCast(enum_info.names.len); - const per_field = @sizeOf(u32); // field type - // 1 byte per field for alignment, rounded up to the nearest 4 bytes - const alignments = if (info.flags.any_aligned_fields) - ((fields_len + 3) / 4) * 4 + const extra = ip.extraDataTrail(Tag.TypeUnion, data); + const captures_len = if (extra.data.flags.any_captures) + ip.extra.items[extra.end] else 0; - break :b @sizeOf(Tag.TypeUnion) + (fields_len * per_field) + alignments; + const per_field = @sizeOf(u32); // field type + // 1 byte per field for alignment, rounded up to the nearest 4 bytes + const alignments = if (extra.data.flags.any_aligned_fields) + ((extra.data.fields_len + 3) / 4) * 4 + else + 0; + break :b @sizeOf(Tag.TypeUnion) + + 4 * (@intFromBool(extra.data.flags.any_captures) + captures_len) + + (extra.data.fields_len * per_field) + alignments; }, .type_function => b: { @@ -7802,7 +8044,6 @@ pub fn destroyNamespace(ip: *InternPool, gpa: Allocator, index: NamespaceIndex) .parent = undefined, .file_scope = undefined, .decl_index = undefined, - .captures = undefined, }; ip.namespaces_free_list.append(gpa, index) catch { // In order to keep `destroyNamespace` a non-fallible function, we ignore memory diff --git a/src/Module.zig b/src/Module.zig index 7f24a2db6d..eb3168e08b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -761,37 +761,6 @@ pub const Namespace = struct { /// the Decl Value has to be resolved as a Type which has a Namespace. /// Value is whether the usingnamespace decl is marked `pub`. usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{}, - /// Allocated into `gpa`. - /// The ordered set of values captured in this type's closure. - /// `closure_get` instructions look up values in this list. - captures: []CaptureValue, - - /// A single value captured in a container's closure. This is not an - /// `InternPool.Index` so we can differentiate between runtime-known values - /// (where only the type is comptime-known) and comptime-known values. - pub const CaptureValue = enum(u32) { - _, - pub const Unwrapped = union(enum) { - /// Index refers to the value. - @"comptime": InternPool.Index, - /// Index refers to the type. - runtime: InternPool.Index, - }; - pub fn wrap(val: Unwrapped) CaptureValue { - return switch (val) { - .@"comptime" => |i| @enumFromInt(@intFromEnum(i)), - .runtime => |i| @enumFromInt((1 << 31) | @intFromEnum(i)), - }; - } - pub fn unwrap(val: CaptureValue) Unwrapped { - const tag: u1 = @intCast(@intFromEnum(val) >> 31); - const raw = @intFromEnum(val); - return switch (tag) { - 0 => .{ .@"comptime" = @enumFromInt(raw) }, - 1 => .{ .runtime = @enumFromInt(@as(u31, @truncate(raw))) }, - }; - } - }; const Index = InternPool.NamespaceIndex; const OptionalIndex = InternPool.OptionalNamespaceIndex; @@ -2130,7 +2099,6 @@ pub fn deinit(zcu: *Zcu) void { while (it.next()) |namespace| { namespace.decls.deinit(gpa); namespace.usingnamespace_set.deinit(gpa); - gpa.free(namespace.captures); } } @@ -3354,7 +3322,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { .parent = .none, .decl_index = undefined, .file_scope = file, - .captures = &.{}, }); const new_namespace = mod.namespacePtr(new_namespace_index); errdefer mod.destroyNamespace(new_namespace_index); diff --git a/src/Sema.zig b/src/Sema.zig index ae5b4360ca..e362c899c6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2670,28 +2670,27 @@ fn analyzeAsInt( } /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`, -/// resolves this into a list of `Namespace.CaptureValue` allocated by `gpa`. -/// Caller owns returned memory. -fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_index: usize, captures_len: u32) ![]Namespace.CaptureValue { - const gpa = sema.gpa; - const parent_captures: []const Namespace.CaptureValue = if (parent_namespace) |p| parent: { - break :parent sema.mod.namespacePtr(p).captures; - } else &.{}; +/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`. +fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { + const zcu = sema.mod; + const ip = &zcu.intern_pool; + const parent_captures: InternPool.CaptureValue.Slice = if (parent_namespace) |p| parent: { + break :parent zcu.namespacePtr(p).ty.getCaptures(zcu); + } else undefined; // never used so `undefined` is safe - const captures = try gpa.alloc(Namespace.CaptureValue, captures_len); - errdefer gpa.free(captures); + const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len); for (sema.code.extra[extra_index..][0..captures_len], captures) |raw, *capture| { const zir_capture: Zir.Inst.Capture = @enumFromInt(raw); capture.* = switch (zir_capture.unwrap()) { - .inst => |inst| Namespace.CaptureValue.wrap(capture: { + .inst => |inst| InternPool.CaptureValue.wrap(capture: { const air_ref = try sema.resolveInst(inst.toRef()); if (try sema.resolveValue(air_ref)) |val| { break :capture .{ .@"comptime" = val.toIntern() }; } break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; }), - .nested => |parent_idx| parent_captures[parent_idx], + .nested => |parent_idx| parent_captures.get(ip)[parent_idx], }; } @@ -2731,7 +2730,7 @@ pub fn getStructType( break :blk decls_len; } else 0; - mod.namespacePtr(namespace).captures = try sema.getCaptures(parent_namespace, extra_index, captures_len); + const captures = try sema.getCaptures(parent_namespace, extra_index, captures_len); extra_index += captures_len; if (small.has_backing_int) { @@ -2761,6 +2760,7 @@ pub fn getStructType( .any_comptime_fields = small.any_comptime_fields, .inits_resolved = false, .any_aligned_fields = small.any_aligned_fields, + .captures = captures, }); return ty; @@ -2801,7 +2801,6 @@ fn zirStructDecl( .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - .captures = &.{}, // Will be set by `getStructType` }); errdefer mod.destroyNamespace(new_namespace_index); @@ -2997,7 +2996,6 @@ fn zirEnumDecl( .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - .captures = captures, }); errdefer if (!done) mod.destroyNamespace(new_namespace_index); @@ -3029,6 +3027,7 @@ fn zirEnumDecl( else .explicit, .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(), + .captures = captures, }); if (sema.builtin_type_target_index != .none) { mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index); @@ -3261,7 +3260,6 @@ fn zirUnionDecl( .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - .captures = captures, }); errdefer mod.destroyNamespace(new_namespace_index); @@ -3291,6 +3289,7 @@ fn zirUnionDecl( .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); @@ -3367,7 +3366,6 @@ fn zirOpaqueDecl( .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - .captures = captures, }); errdefer mod.destroyNamespace(new_namespace_index); @@ -3375,6 +3373,7 @@ fn zirOpaqueDecl( .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); @@ -17287,12 +17286,13 @@ fn zirThis( fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const mod = sema.mod; - const captures = mod.namespacePtr(block.namespace).captures; + const ip = &mod.intern_pool; + const captures = mod.namespacePtr(block.namespace).ty.getCaptures(mod); const src_node: i32 = @bitCast(extended.operand); const src = LazySrcLoc.nodeOffset(src_node); - const capture_ty = switch (captures[extended.small].unwrap()) { + const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) { .@"comptime" => |index| return Air.internedToRef(index), .runtime => |index| index, }; @@ -21360,6 +21360,7 @@ fn zirReify( .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); @@ -21450,7 +21451,6 @@ fn zirReify( .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - .captures = &.{}, }); errdefer mod.destroyNamespace(new_namespace_index); @@ -21458,6 +21458,7 @@ fn zirReify( .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); @@ -21659,7 +21660,6 @@ fn zirReify( .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - .captures = &.{}, }); errdefer mod.destroyNamespace(new_namespace_index); @@ -21688,6 +21688,7 @@ fn zirReify( }, .field_types = union_fields.items(.type), .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{}, + .captures = &.{}, }); new_decl.ty = Type.type; @@ -21849,6 +21850,7 @@ fn reifyStruct( .any_default_inits = true, .inits_resolved = true, .any_aligned_fields = true, + .captures = &.{}, }); // TODO: figure out InternPool removals for incremental compilation //errdefer ip.remove(ty); @@ -37404,6 +37406,7 @@ fn generateUnionTagTypeNumbered( .values = enum_field_vals, .tag_mode = .explicit, .zir_index = .none, + .captures = &.{}, }); new_decl.ty = Type.type; @@ -37455,6 +37458,7 @@ fn generateUnionTagTypeSimple( .values = &.{}, .tag_mode = .auto, .zir_index = .none, + .captures = &.{}, }); const new_decl = mod.declPtr(new_decl_index); diff --git a/src/type.zig b/src/type.zig index 2021a1aaea..d19ad6f02e 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3294,6 +3294,18 @@ pub const Type = struct { }; } + /// Given a namespace type, returns its list of caotured values. + pub fn getCaptures(ty: Type, zcu: *const Zcu) InternPool.CaptureValue.Slice { + const ip = &zcu.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + .struct_type => ip.loadStructType(ty.toIntern()).captures, + .union_type => ip.loadUnionType(ty.toIntern()).captures, + .enum_type => ip.loadEnumType(ty.toIntern()).captures, + .opaque_type => ip.loadOpaqueType(ty.toIntern()).captures, + else => unreachable, + }; + } + pub const @"u1": Type = .{ .ip_index = .u1_type }; pub const @"u8": Type = .{ .ip_index = .u8_type }; pub const @"u16": Type = .{ .ip_index = .u16_type };