1//! All interned objects have both a value and a type.
2//! This data structure is self-contained.
3const InternPool = @This();
4
5const builtin = @import("builtin");
6const build_options = @import("build_options");
7
8const std = @import("std");
9const Io = std.Io;
10const Allocator = std.mem.Allocator;
11const assert = std.debug.assert;
12const BigIntConst = std.math.big.int.Const;
13const BigIntMutable = std.math.big.int.Mutable;
14const Cache = std.Build.Cache;
15const Limb = std.math.big.Limb;
16const Hash = std.hash.Wyhash;
17const Zir = std.zig.Zir;
18
19const Zcu = @import("Zcu.zig");
20const TypeClass = @import("Type.zig").Class;
21
22/// One item per thread, indexed by `tid`, which is dense and unique per thread.
23locals: []Local,
24/// Length must be a power of two and represents the number of simultaneous
25/// writers that can mutate any single sharded data structure.
26shards: []Shard,
27/// Key is the error name, index is the error tag value. Index 0 has a length-0 string.
28global_error_set: GlobalErrorSet,
29/// Cached number of active bits in a `tid`.
30tid_width: if (single_threaded) u0 else std.math.Log2Int(u32),
31/// Cached shift amount to put a `tid` in the top bits of a 30-bit value.
32tid_shift_30: if (single_threaded) u0 else std.math.Log2Int(u32),
33/// Cached shift amount to put a `tid` in the top bits of a 31-bit value.
34tid_shift_31: if (single_threaded) u0 else std.math.Log2Int(u32),
35/// Cached shift amount to put a `tid` in the top bits of a 32-bit value.
36tid_shift_32: if (single_threaded) u0 else std.math.Log2Int(u32),
37
38/// Dependencies on the source code hash associated with a ZIR instruction.
39/// * For a `declaration`, this is the entire declaration body.
40/// * For a `struct_decl`, `union_decl`, etc, this is the source of the fields (but not declarations).
41/// * For a `func`, this is the source of the full function signature.
42/// These are also invalidated if tracking fails for this instruction.
43/// Value is index into `dep_entries` of the first dependency on this hash.
44src_hash_deps: std.array_hash_map.Auto(TrackedInst.Index, DepEntry.Index),
45/// Dependencies on the value of a Nav.
46/// Value is index into `dep_entries` of the first dependency on this Nav value.
47nav_val_deps: std.array_hash_map.Auto(Nav.Index, DepEntry.Index),
48/// Dependencies on the type of a Nav.
49/// Value is index into `dep_entries` of the first dependency on this Nav value.
50nav_ty_deps: std.array_hash_map.Auto(Nav.Index, DepEntry.Index),
51/// Dependencies on a function's inferred error set. Key is the function body, not the IES.
52/// Value is index into `dep_entries` of the first dependency on this function's IES.
53func_ies_deps: std.array_hash_map.Auto(Index, DepEntry.Index),
54/// Dependencies on the resolved layout of a `struct`, `union`, or `enum` type.
55/// Value is index into `dep_entries` of the first dependency on this type's layout.
56type_layout_deps: std.array_hash_map.Auto(Index, DepEntry.Index),
57/// Dependencies on the resolved default field values of a `struct` type.
58/// Value is index into `dep_entries` of the first dependency on this type's inits.
59struct_defaults_deps: std.array_hash_map.Auto(Index, DepEntry.Index),
60/// Dependencies on a Zig or ZON source file. Triggered by `@import`.
61/// * For ZON source files, the dependency is invalidated if the file changes at all. The `@import`
62/// must be re-analyzed to return the new data structure.
63/// * For Zig source files, the dependency is invalidated if the file's root struct type changes
64/// (which can only happen because the `.main_struct_inst` got lost). The `@import` must be
65/// re-analyzed to return the new type.
66/// Value is index into `dep_entries` of the first dependency on this Zig/ZON file.
67source_file_deps: std.array_hash_map.Auto(FileIndex, DepEntry.Index),
68/// Dependencies on an embedded file.
69/// Introduced by `@embedFile`; invalidated when the file changes.
70/// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`.
71embed_file_deps: std.array_hash_map.Auto(Zcu.EmbedFile.Index, DepEntry.Index),
72/// Dependencies on the full set of names in a ZIR namespace.
73/// Key refers to a `struct_decl`, `union_decl`, etc.
74/// Value is index into `dep_entries` of the first dependency on this namespace.
75namespace_deps: std.array_hash_map.Auto(TrackedInst.Index, DepEntry.Index),
76/// Dependencies on the (non-)existence of some name in a namespace.
77/// Value is index into `dep_entries` of the first dependency on this name.
78namespace_name_deps: std.array_hash_map.Auto(NamespaceNameKey, DepEntry.Index),
79// Dependencies on the value of fields memoized on `Zcu` (`panic_messages` etc).
80// If set, these are indices into `dep_entries` of the first dependency on this state.
81memoized_state_main_deps: DepEntry.Index.Optional,
82memoized_state_panic_deps: DepEntry.Index.Optional,
83memoized_state_va_list_deps: DepEntry.Index.Optional,
84memoized_state_assembly_deps: DepEntry.Index.Optional,
85
86/// Given a `Depender`, points to an entry in `dep_entries` whose `depender`
87/// matches. The `next_dependee` field can be used to iterate all such entries
88/// and remove them from the corresponding lists.
89first_dependency: std.array_hash_map.Auto(AnalUnit, DepEntry.Index),
90
91/// Stores dependency information. The hashmaps declared above are used to look
92/// up entries in this list as required. This is not stored in `extra` so that
93/// we can use `free_dep_entries` to track free indices, since dependencies are
94/// removed frequently.
95dep_entries: std.ArrayList(DepEntry),
96/// Stores unused indices in `dep_entries` which can be reused without a full
97/// garbage collection pass.
98free_dep_entries: std.ArrayList(DepEntry.Index),
99
100/// Whether a single-threaded intern pool impl is in use.
101pub const single_threaded = switch (build_options.io_mode) {
102 .threaded => builtin.single_threaded,
103 .evented => false, // even without threads, evented can be access from multiple tasks at a time
104};
105
106pub const empty: InternPool = .{
107 .locals = &.{},
108 .shards = &.{},
109 .global_error_set = .empty,
110 .tid_width = 0,
111 .tid_shift_30 = 0,
112 .tid_shift_31 = 0,
113 .tid_shift_32 = 0,
114 .src_hash_deps = .empty,
115 .nav_val_deps = .empty,
116 .nav_ty_deps = .empty,
117 .func_ies_deps = .empty,
118 .type_layout_deps = .empty,
119 .struct_defaults_deps = .empty,
120 .source_file_deps = .empty,
121 .embed_file_deps = .empty,
122 .namespace_deps = .empty,
123 .namespace_name_deps = .empty,
124 .memoized_state_main_deps = .none,
125 .memoized_state_panic_deps = .none,
126 .memoized_state_va_list_deps = .none,
127 .memoized_state_assembly_deps = .none,
128 .first_dependency = .empty,
129 .dep_entries = .empty,
130 .free_dep_entries = .empty,
131};
132
133/// A `TrackedInst.Index` provides a single, unchanging reference to a ZIR instruction across a whole
134/// compilation. From this index, you can acquire a `TrackedInst`, which containss a reference to both
135/// the file which the instruction lives in, and the instruction index itself, which is updated on
136/// incremental updates by `Zcu.updateZirRefs`.
137pub const TrackedInst = extern struct {
138 file: FileIndex,
139 inst: Zir.Inst.Index,
140
141 /// It is possible on an incremental update that we "lose" a ZIR instruction: some tracked `%x` in
142 /// the old ZIR failed to map to any `%y` in the new ZIR. For this reason, we actually store values
143 /// of type `MaybeLost`, which uses `ZirIndex.lost` to represent this case. `Index.resolve` etc
144 /// return `null` when the `TrackedInst` being resolved has been lost.
145 pub const MaybeLost = extern struct {
146 file: FileIndex,
147 inst: ZirIndex,
148 pub const ZirIndex = enum(u32) {
149 /// Tracking failed for this ZIR instruction. Uses of it should fail.
150 lost = std.math.maxInt(u32),
151 _,
152 pub fn unwrap(inst: ZirIndex) ?Zir.Inst.Index {
153 return switch (inst) {
154 .lost => null,
155 _ => @fromBackingInt(@intCast(@backingInt(inst))),
156 };
157 }
158 pub fn wrap(inst: Zir.Inst.Index) ZirIndex {
159 return @fromBackingInt(@intCast(@backingInt(inst)));
160 }
161 };
162 comptime {
163 // The fields should be tightly packed. See also serialiation logic in `Compilation.saveState`.
164 assert(@sizeOf(@This()) == @sizeOf(FileIndex) + @sizeOf(ZirIndex));
165 }
166 };
167
168 pub const Index = enum(u32) {
169 _,
170 pub fn resolveFull(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) ?TrackedInst {
171 const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip);
172 const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire();
173 const maybe_lost = tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index];
174 return .{
175 .file = maybe_lost.file,
176 .inst = maybe_lost.inst.unwrap() orelse return null,
177 };
178 }
179 pub fn resolveFile(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) FileIndex {
180 const tracked_inst_unwrapped = tracked_inst_index.unwrap(ip);
181 const tracked_insts = ip.getLocalShared(tracked_inst_unwrapped.tid).tracked_insts.acquire();
182 const maybe_lost = tracked_insts.view().items(.@"0")[tracked_inst_unwrapped.index];
183 return maybe_lost.file;
184 }
185 pub fn resolve(i: TrackedInst.Index, ip: *const InternPool) ?Zir.Inst.Index {
186 return (i.resolveFull(ip) orelse return null).inst;
187 }
188
189 pub fn toOptional(i: TrackedInst.Index) Optional {
190 return @fromBackingInt(@intCast(@backingInt(i)));
191 }
192 pub const Optional = enum(u32) {
193 none = std.math.maxInt(u32),
194 _,
195 pub fn unwrap(opt: Optional) ?TrackedInst.Index {
196 return switch (opt) {
197 .none => null,
198 _ => @fromBackingInt(@intCast(@backingInt(opt))),
199 };
200 }
201
202 const debug_state = InternPool.debug_state;
203 };
204
205 pub const Unwrapped = struct {
206 tid: Zcu.PerThread.Id,
207 index: u32,
208
209 pub fn wrap(unwrapped: Unwrapped, ip: *const InternPool) TrackedInst.Index {
210 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
211 assert(unwrapped.index <= ip.getIndexMask(u32));
212 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_32) |
213 unwrapped.index));
214 }
215 };
216 pub fn unwrap(tracked_inst_index: TrackedInst.Index, ip: *const InternPool) Unwrapped {
217 return .{
218 .tid = @fromBackingInt(@intCast(@backingInt(tracked_inst_index) >> ip.tid_shift_32 & ip.getTidMask())),
219 .index = @backingInt(tracked_inst_index) & ip.getIndexMask(u32),
220 };
221 }
222
223 const debug_state = InternPool.debug_state;
224 };
225};
226
227pub fn trackZir(
228 ip: *InternPool,
229 gpa: Allocator,
230 io: Io,
231 tid: Zcu.PerThread.Id,
232 key: TrackedInst,
233) Allocator.Error!TrackedInst.Index {
234 const maybe_lost_key: TrackedInst.MaybeLost = .{
235 .file = key.file,
236 .inst = TrackedInst.MaybeLost.ZirIndex.wrap(key.inst),
237 };
238 const full_hash = Hash.hash(0, std.mem.asBytes(&maybe_lost_key));
239 const hash: u32 = @truncate(full_hash >> 32);
240 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
241 var map = shard.shared.tracked_inst_map.acquire();
242 const Map = @TypeOf(map);
243 var map_mask = map.header().mask();
244 var map_index = hash;
245 while (true) : (map_index += 1) {
246 map_index &= map_mask;
247 const entry = &map.entries[map_index];
248 const index = entry.acquire().unwrap() orelse break;
249 if (entry.hash != hash) continue;
250 if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index;
251 }
252 shard.mutate.tracked_inst_map.mutex.lock(io, tid);
253 defer shard.mutate.tracked_inst_map.mutex.unlock(io);
254 if (map.entries != shard.shared.tracked_inst_map.entries) {
255 map = shard.shared.tracked_inst_map;
256 map_mask = map.header().mask();
257 map_index = hash;
258 }
259 while (true) : (map_index += 1) {
260 map_index &= map_mask;
261 const entry = &map.entries[map_index];
262 const index = entry.acquire().unwrap() orelse break;
263 if (entry.hash != hash) continue;
264 if (std.meta.eql(index.resolveFull(ip) orelse continue, key)) return index;
265 }
266 defer shard.mutate.tracked_inst_map.len += 1;
267 const local = ip.getLocal(tid);
268 const list = local.getMutableTrackedInsts(gpa, io);
269 try list.ensureUnusedCapacity(1);
270 const map_header = map.header().*;
271 if (shard.mutate.tracked_inst_map.len < map_header.capacity * 3 / 5) {
272 const entry = &map.entries[map_index];
273 entry.hash = hash;
274 const index = (TrackedInst.Index.Unwrapped{
275 .tid = tid,
276 .index = list.mutate.len,
277 }).wrap(ip);
278 list.appendAssumeCapacity(.{maybe_lost_key});
279 entry.release(index.toOptional());
280 return index;
281 }
282 const arena_state = &local.mutate.arena;
283 var arena = arena_state.promote(gpa);
284 defer arena_state.* = arena.state;
285 const new_map_capacity = map_header.capacity * 2;
286 const new_map_buf = try arena.allocator().alignedAlloc(
287 u8,
288 .fromByteUnits(Map.alignment),
289 Map.entries_offset + new_map_capacity * @sizeOf(Map.Entry),
290 );
291 const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) };
292 new_map.header().* = .{ .capacity = new_map_capacity };
293 @memset(new_map.entries[0..new_map_capacity], .{ .value = .none, .hash = undefined });
294 const new_map_mask = new_map.header().mask();
295 map_index = 0;
296 while (map_index < map_header.capacity) : (map_index += 1) {
297 const entry = &map.entries[map_index];
298 const index = entry.value.unwrap() orelse continue;
299 const item_hash = entry.hash;
300 var new_map_index = item_hash;
301 while (true) : (new_map_index += 1) {
302 new_map_index &= new_map_mask;
303 const new_entry = &new_map.entries[new_map_index];
304 if (new_entry.value != .none) continue;
305 new_entry.* = .{
306 .value = index.toOptional(),
307 .hash = item_hash,
308 };
309 break;
310 }
311 }
312 map = new_map;
313 map_index = hash;
314 while (true) : (map_index += 1) {
315 map_index &= new_map_mask;
316 if (map.entries[map_index].value == .none) break;
317 }
318 const index = (TrackedInst.Index.Unwrapped{
319 .tid = tid,
320 .index = list.mutate.len,
321 }).wrap(ip);
322 list.appendAssumeCapacity(.{maybe_lost_key});
323 map.entries[map_index] = .{ .value = index.toOptional(), .hash = hash };
324 shard.shared.tracked_inst_map.release(new_map);
325 return index;
326}
327
328/// At the start of an incremental update, we update every entry in `tracked_insts` to include
329/// the new ZIR index. Once this is done, we must update the hashmap metadata so that lookups
330/// return correct entries where they already exist.
331pub fn rehashTrackedInsts(
332 ip: *InternPool,
333 gpa: Allocator,
334 io: Io,
335 tid: Zcu.PerThread.Id,
336) Allocator.Error!void {
337 assert(tid == .main); // we shouldn't have any other threads active right now
338
339 // TODO: this function doesn't handle OOM well. What should it do?
340
341 // We don't lock anything, as this function assumes that no other thread is
342 // accessing `tracked_insts`. This is necessary because we're going to be
343 // iterating the `TrackedInst`s in each `Local`, so we have to know that
344 // none will be added as we work.
345
346 // Figure out how big each shard need to be and store it in its mutate `len`.
347 for (ip.shards) |*shard| shard.mutate.tracked_inst_map.len = 0;
348 for (ip.locals) |*local| {
349 // `getMutableTrackedInsts` is okay only because no other thread is currently active.
350 // We need the `mutate` for the len.
351 for (local.getMutableTrackedInsts(gpa, io).viewAllowEmpty().items(.@"0")) |tracked_inst| {
352 if (tracked_inst.inst == .lost) continue; // we can ignore this one!
353 const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst));
354 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
355 shard.mutate.tracked_inst_map.len += 1;
356 }
357 }
358
359 const Map = Shard.Map(TrackedInst.Index.Optional);
360
361 const arena_state = &ip.getLocal(tid).mutate.arena;
362
363 // We know how big each shard must be, so ensure we have the capacity we need.
364 for (ip.shards) |*shard| {
365 const want_capacity = if (shard.mutate.tracked_inst_map.len == 0) 0 else cap: {
366 // We need to return a capacity of at least 2 to make sure we don't have the `Map(...).empty` value.
367 // For this reason, note the `+ 1` in the below expression. This matches the behavior of `trackZir`.
368 break :cap std.math.ceilPowerOfTwo(u32, shard.mutate.tracked_inst_map.len * 5 / 3 + 1) catch unreachable;
369 };
370 const have_capacity = shard.shared.tracked_inst_map.header().capacity; // no acquire because we hold the mutex
371 if (have_capacity >= want_capacity) {
372 if (have_capacity == 1) {
373 // The map is `.empty` -- we can't memset the entries, or we'll segfault, because
374 // the buffer is secretly constant.
375 } else {
376 @memset(shard.shared.tracked_inst_map.entries[0..have_capacity], .{ .value = .none, .hash = undefined });
377 }
378 continue;
379 }
380 var arena = arena_state.promote(gpa);
381 defer arena_state.* = arena.state;
382 const new_map_buf = try arena.allocator().alignedAlloc(
383 u8,
384 .fromByteUnits(Map.alignment),
385 Map.entries_offset + want_capacity * @sizeOf(Map.Entry),
386 );
387 const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) };
388 new_map.header().* = .{ .capacity = want_capacity };
389 @memset(new_map.entries[0..want_capacity], .{ .value = .none, .hash = undefined });
390 shard.shared.tracked_inst_map.release(new_map);
391 }
392
393 // Now, actually insert the items.
394 for (ip.locals, 0..) |*local, local_tid| {
395 // `getMutableTrackedInsts` is okay only because no other thread is currently active.
396 // We need the `mutate` for the len.
397 for (local.getMutableTrackedInsts(gpa, io).viewAllowEmpty().items(.@"0"), 0..) |tracked_inst, local_inst_index| {
398 if (tracked_inst.inst == .lost) continue; // we can ignore this one!
399 const full_hash = Hash.hash(0, std.mem.asBytes(&tracked_inst));
400 const hash: u32 = @truncate(full_hash >> 32);
401 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
402 const map = shard.shared.tracked_inst_map; // no acquire because we hold the mutex
403 const map_mask = map.header().mask();
404 var map_index = hash;
405 const entry = while (true) : (map_index += 1) {
406 map_index &= map_mask;
407 const entry = &map.entries[map_index];
408 if (entry.acquire() == .none) break entry;
409 };
410 const index = TrackedInst.Index.Unwrapped.wrap(.{
411 .tid = @fromBackingInt(@intCast(local_tid)),
412 .index = @intCast(local_inst_index),
413 }, ip);
414 entry.hash = hash;
415 entry.release(index.toOptional());
416 }
417 }
418}
419
420/// Analysis Unit. Represents a single entity which undergoes semantic analysis.
421/// This is the "source" of an incremental dependency edge.
422pub const AnalUnit = packed struct(u64) {
423 kind: Kind,
424 id: u32,
425
426 pub const Kind = enum(u32) {
427 @"comptime",
428 nav_val,
429 nav_ty,
430 type_layout,
431 struct_defaults,
432 func,
433 memoized_state,
434 };
435
436 pub const Unwrapped = union(Kind) {
437 /// This `AnalUnit` analyzes the body of the given `comptime` declaration.
438 @"comptime": ComptimeUnit.Id,
439 /// This `AnalUnit` resolves the value of the given `Nav`.
440 nav_val: Nav.Index,
441 /// This `AnalUnit` resolves the type of the given `Nav`.
442 nav_ty: Nav.Index,
443 /// This `AnalUnit` resolves the layout of the given `struct`, `union`, or `enum` type.
444 type_layout: InternPool.Index,
445 /// This `AnalUnit` resolves the default field values of the given `struct` type.
446 struct_defaults: InternPool.Index,
447 /// This `AnalUnit` analyzes the body of the given runtime function.
448 func: InternPool.Index,
449 /// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.
450 memoized_state: MemoizedStateStage,
451 };
452
453 pub fn unwrap(au: AnalUnit) Unwrapped {
454 return switch (au.kind) {
455 inline else => |tag| @unionInit(
456 Unwrapped,
457 @tagName(tag),
458 @fromBackingInt(@intCast(au.id)),
459 ),
460 };
461 }
462 pub fn wrap(raw: Unwrapped) AnalUnit {
463 return switch (raw) {
464 inline else => |id, tag| .{
465 .kind = tag,
466 .id = @backingInt(id),
467 },
468 };
469 }
470
471 pub fn toOptional(as: AnalUnit) Optional {
472 return @fromBackingInt(@intCast(@as(u64, @bitCast(as))));
473 }
474 pub const Optional = enum(u64) {
475 none = std.math.maxInt(u64),
476 _,
477 pub fn unwrap(opt: Optional) ?AnalUnit {
478 return switch (opt) {
479 .none => null,
480 _ => @bitCast(@backingInt(opt)),
481 };
482 }
483 };
484};
485
486pub const MemoizedStateStage = enum(u32) {
487 /// Everything other than panics and `VaList`.
488 main,
489 /// Everything within `std.lang.Panic`.
490 /// Since the panic handler is user-provided, this must be able to reference the other memoized state.
491 panic,
492 /// Specifically `std.lang.VaList`. See `Zcu.StdLangDecl.stage`.
493 va_list,
494 /// Everything within `std.lang.assembly`. See `Zcu.StdLangDecl.stage`.
495 assembly,
496};
497
498pub const ComptimeUnit = extern struct {
499 zir_index: TrackedInst.Index,
500 namespace: NamespaceIndex,
501
502 comptime {
503 assert(std.meta.hasUniqueRepresentation(ComptimeUnit));
504 }
505
506 pub const Id = enum(u32) {
507 _,
508 const Unwrapped = struct {
509 tid: Zcu.PerThread.Id,
510 index: u32,
511 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) ComptimeUnit.Id {
512 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
513 assert(unwrapped.index <= ip.getIndexMask(u32));
514 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_32) |
515 unwrapped.index));
516 }
517 };
518 fn unwrap(id: Id, ip: *const InternPool) Unwrapped {
519 return .{
520 .tid = @fromBackingInt(@intCast(@backingInt(id) >> ip.tid_shift_32 & ip.getTidMask())),
521 .index = @backingInt(id) & ip.getIndexMask(u31),
522 };
523 }
524
525 const debug_state = InternPool.debug_state;
526 };
527};
528
529/// Named Addressable Value. Represents a global value with a name and address. This name may be
530/// generated, and the type (and hence address) may be comptime-only. A `Nav` whose type has runtime
531/// bits is sent to the linker to be emitted to the binary.
532///
533/// * Every ZIR `declaration` which is not a `comptime` declaration has a `Nav` (post-instantiation)
534/// which stores the declaration's resolved value.
535/// * Generic instances have a `Nav` corresponding to the instantiated function.
536/// * `@extern` calls create a `Nav` whose value is a `.@"extern"`.
537///
538/// This data structure is optimized for the `analysis_info != null` case, because this is much more
539/// common in practice; the other case is used only for externs and for generic instances. At the time
540/// of writing, in the compiler itself, around 74% of all `Nav`s have `analysis_info != null`.
541/// (Specifically, 104225 / 140923)
542///
543/// `Nav.Repr` is the in-memory representation.
544pub const Nav = struct {
545 /// The unqualified name of this `Nav`. Namespace lookups use this name, and error messages may use it.
546 /// Additionally, extern `Nav`s (i.e. those whose value is an `extern`) use this name.
547 name: NullTerminatedString,
548 /// The fully-qualified name of this `Nav`.
549 fqn: NullTerminatedString,
550 /// This field is populated iff this `Nav` is resolved by semantic analysis.
551 /// If this is `null`, then `resolved` is *not* `null`.
552 analysis: ?struct {
553 namespace: NamespaceIndex,
554 zir_index: TrackedInst.Index,
555 /// Initially `false`. Set to `true` by `setWantNavAnalysis`.
556 wanted: bool,
557 },
558 /// If this is `null`, then `analysis` is *not* `null`, and semantic analysis is required to
559 /// resolve the type and value of this `Nav`. Otherwise, the type is resolved---therefore,
560 /// `Nav.resolved.?.type` is never `.none`. However, the *value* may not be resolved yet even
561 /// if this field is not `null`---see `Resolved.value` for details.
562 resolved: ?Resolved,
563
564 pub const Resolved = struct {
565 /// This is never `.none`
566 type: InternPool.Index,
567 @"align": Alignment,
568 @"linksection": OptionalNullTerminatedString,
569 @"addrspace": std.lang.AddressSpace,
570 @"const": bool,
571 @"threadlocal": bool,
572 /// This field is whether this `Nav` is a literal `extern` definition.
573 /// It does *not* tell you whether this might alias an extern fn (see #21027).
574 is_extern_decl: bool,
575 /// If the type is resolved but not the value, this is `.none`. In that case, the value will
576 /// be resolved by semantic analysis, so `Nav.analysis` is definitely not `null`.
577 ///
578 /// If this is an extern, the special key `Key.@"extern"` is used.
579 ///
580 /// If this is a variable (`Resolved.@"const" == false`) and not an extern, then this value
581 /// is the global variable's initializer; the value loaded from the variable at runtime may
582 /// of course be different.
583 value: InternPool.Index,
584 };
585
586 /// If the value of this `Nav` is resolved and is an extern, returns the `Key.Extern`. If the
587 /// value is *not* an extern, *or* if the value is not yet resolved (only the type is), returns
588 /// `null`.
589 ///
590 /// This logic works because the frontend ensures that if a `Nav` *might* be extern, its value
591 /// is resolved more eagerly (see logic in `Sema.analyzeNavRefInner`). Therefore, if we see that
592 /// the value is not yet resolved, we know the frontend determined that the `Nav` is definitely
593 /// *not* extern.
594 ///
595 /// This function is only intended be used by the compiler backend (codegen/link). The guarantee
596 /// mentioned above does not necessarily hold in the compiler frontend (if we haven't reached
597 /// `Sema.analyzeNavRefInner` yet).
598 ///
599 /// Asserts that `nav.resolved != null`.
600 pub fn getExtern(nav: Nav, ip: *const InternPool) ?Key.Extern {
601 const r = nav.resolved.?;
602 if (r.value == .none) return null;
603 return switch (ip.indexToKey(r.value)) {
604 .@"extern" => |e| e,
605 else => null,
606 };
607 }
608
609 /// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.
610 /// This is a `declaration`.
611 pub fn srcInst(nav: Nav, ip: *const InternPool) TrackedInst.Index {
612 if (nav.analysis) |a| {
613 return a.zir_index;
614 }
615 // A `Nav` which does not undergo analysis always has a resolved value.
616 return switch (ip.indexToKey(nav.resolved.?.value)) {
617 .func => |func| {
618 // Since `analysis` was not populated, this must be an instantiation.
619 // Go up to the generic owner and consult *its* `analysis` field.
620 const go_nav = ip.getNav(ip.indexToKey(func.generic_owner).func.owner_nav);
621 return go_nav.analysis.?.zir_index;
622 },
623 .@"extern" => |@"extern"| @"extern".zir_index, // extern / @extern
624 else => unreachable,
625 };
626 }
627
628 pub const Index = enum(u32) {
629 _,
630 pub const Optional = enum(u32) {
631 none = std.math.maxInt(u32),
632 _,
633 pub fn unwrap(opt: Optional) ?Nav.Index {
634 return switch (opt) {
635 .none => null,
636 _ => @fromBackingInt(@intCast(@backingInt(opt))),
637 };
638 }
639
640 const debug_state = InternPool.debug_state;
641 };
642 pub fn toOptional(i: Nav.Index) Optional {
643 return @fromBackingInt(@intCast(@backingInt(i)));
644 }
645 const Unwrapped = struct {
646 tid: Zcu.PerThread.Id,
647 index: u32,
648
649 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) Nav.Index {
650 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
651 assert(unwrapped.index <= ip.getIndexMask(u30));
652 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_30) |
653 unwrapped.index));
654 }
655 };
656 fn unwrap(nav_index: Nav.Index, ip: *const InternPool) Unwrapped {
657 return .{
658 .tid = @fromBackingInt(@intCast(@backingInt(nav_index) >> ip.tid_shift_30 & ip.getTidMask())),
659 .index = @backingInt(nav_index) & ip.getIndexMask(u30),
660 };
661 }
662
663 const debug_state = InternPool.debug_state;
664 };
665
666 /// The compact in-memory representation of a `Nav`.
667 /// 30 bytes.
668 const Repr = struct {
669 name: NullTerminatedString,
670 fqn: NullTerminatedString,
671 // The following 2 fields are either both populated, or both `.none`.
672 analysis_namespace: OptionalNamespaceIndex,
673 analysis_zir_index: TrackedInst.Index.Optional,
674 type: InternPool.Index,
675 value: InternPool.Index,
676 @"linksection": OptionalNullTerminatedString,
677 bits: Bits,
678
679 const Bits = packed struct(u16) {
680 @"align": Alignment,
681 @"addrspace": std.lang.AddressSpace,
682 @"const": bool,
683 @"threadlocal": bool,
684 is_extern_decl: bool,
685 want_analysis: bool,
686 _: u1 = 0,
687 };
688
689 fn unpack(repr: Repr) Nav {
690 return .{
691 .name = repr.name,
692 .fqn = repr.fqn,
693 .analysis = if (repr.analysis_namespace.unwrap()) |namespace| .{
694 .namespace = namespace,
695 .zir_index = repr.analysis_zir_index.unwrap().?,
696 .wanted = repr.bits.want_analysis,
697 } else a: {
698 assert(repr.analysis_zir_index == .none);
699 break :a null;
700 },
701 .resolved = if (repr.type == .none) null else .{
702 .type = repr.type,
703 .@"align" = repr.bits.@"align",
704 .@"linksection" = repr.@"linksection",
705 .@"addrspace" = repr.bits.@"addrspace",
706 .@"const" = repr.bits.@"const",
707 .@"threadlocal" = repr.bits.@"threadlocal",
708 .is_extern_decl = repr.bits.is_extern_decl,
709 .value = repr.value,
710 },
711 };
712 }
713 };
714
715 fn pack(nav: Nav) Repr {
716 // Note that even if `nav.resolved == null`, we do not set any fields to `undefined`, even
717 // though they should not be used. This is to avoid writing undefined bytes to disk when
718 // serializing buffers.
719 return .{
720 .name = nav.name,
721 .fqn = nav.fqn,
722 .analysis_namespace = if (nav.analysis) |a| a.namespace.toOptional() else .none,
723 .analysis_zir_index = if (nav.analysis) |a| a.zir_index.toOptional() else .none,
724 .type = if (nav.resolved) |r| r.type else .none,
725 .value = if (nav.resolved) |r| r.value else .none,
726 .@"linksection" = if (nav.resolved) |r| r.@"linksection" else .none,
727 .bits = if (nav.resolved) |r| .{
728 .@"align" = r.@"align",
729 .@"addrspace" = r.@"addrspace",
730 .@"const" = r.@"const",
731 .@"threadlocal" = r.@"threadlocal",
732 .is_extern_decl = r.is_extern_decl,
733 .want_analysis = if (nav.analysis) |a| a.wanted else false,
734 } else .{
735 .@"align" = .none,
736 .@"addrspace" = .generic,
737 .@"const" = false,
738 .@"threadlocal" = false,
739 .is_extern_decl = false,
740 .want_analysis = if (nav.analysis) |a| a.wanted else false,
741 },
742 };
743 }
744};
745
746pub const Dependee = union(enum) {
747 src_hash: TrackedInst.Index,
748 nav_val: Nav.Index,
749 nav_ty: Nav.Index,
750 /// Index is the function, not its IES.
751 func_ies: Index,
752 type_layout: Index,
753 struct_defaults: Index,
754 source_file: FileIndex,
755 embed_file: Zcu.EmbedFile.Index,
756 namespace: TrackedInst.Index,
757 namespace_name: NamespaceNameKey,
758 memoized_state: MemoizedStateStage,
759};
760
761pub fn removeDependenciesForDepender(ip: *InternPool, gpa: Allocator, depender: AnalUnit) void {
762 var opt_idx = (ip.first_dependency.fetchSwapRemove(depender) orelse return).value.toOptional();
763
764 while (opt_idx.unwrap()) |idx| {
765 const dep = ip.dep_entries.items[@backingInt(idx)];
766 opt_idx = dep.next_dependee;
767
768 const prev_idx = dep.prev.unwrap() orelse {
769 // This entry is the start of a list in some `*_deps`.
770 // We cannot easily remove this mapping, so this must remain as a dummy entry.
771 ip.dep_entries.items[@backingInt(idx)].depender = .none;
772 continue;
773 };
774
775 ip.dep_entries.items[@backingInt(prev_idx)].next = dep.next;
776 if (dep.next.unwrap()) |next_idx| {
777 ip.dep_entries.items[@backingInt(next_idx)].prev = dep.prev;
778 }
779
780 ip.free_dep_entries.append(gpa, idx) catch {
781 // This memory will be reclaimed on the next garbage collection.
782 // Thus, we do not need to propagate this error.
783 };
784 }
785}
786
787pub const DependencyIterator = struct {
788 ip: *const InternPool,
789 next_entry: DepEntry.Index.Optional,
790 pub fn next(it: *DependencyIterator) ?AnalUnit {
791 while (true) {
792 const idx = it.next_entry.unwrap() orelse return null;
793 const entry = it.ip.dep_entries.items[@backingInt(idx)];
794 it.next_entry = entry.next;
795 if (entry.depender.unwrap()) |depender| return depender;
796 }
797 }
798};
799
800pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyIterator {
801 const first_entry = switch (dependee) {
802 .src_hash => |x| ip.src_hash_deps.get(x),
803 .nav_val => |x| ip.nav_val_deps.get(x),
804 .nav_ty => |x| ip.nav_ty_deps.get(x),
805 .func_ies => |x| ip.func_ies_deps.get(x),
806 .type_layout => |x| ip.type_layout_deps.get(x),
807 .struct_defaults => |x| ip.struct_defaults_deps.get(x),
808 .source_file => |x| ip.source_file_deps.get(x),
809 .embed_file => |x| ip.embed_file_deps.get(x),
810 .namespace => |x| ip.namespace_deps.get(x),
811 .namespace_name => |x| ip.namespace_name_deps.get(x),
812 .memoized_state => |stage| switch (stage) {
813 .main => ip.memoized_state_main_deps.unwrap(),
814 .panic => ip.memoized_state_panic_deps.unwrap(),
815 .va_list => ip.memoized_state_va_list_deps.unwrap(),
816 .assembly => ip.memoized_state_assembly_deps.unwrap(),
817 },
818 } orelse return .{
819 .ip = ip,
820 .next_entry = .none,
821 };
822 return .{
823 .ip = ip,
824 .next_entry = first_entry.toOptional(),
825 };
826}
827
828pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, dependee: Dependee) Allocator.Error!void {
829 const first_depender_dep: DepEntry.Index.Optional = if (ip.first_dependency.get(depender)) |idx| dep: {
830 // The entry already exists, so there is capacity to overwrite it later.
831 break :dep idx.toOptional();
832 } else none: {
833 // Ensure there is capacity available to add this dependency later.
834 try ip.first_dependency.ensureUnusedCapacity(gpa, 1);
835 break :none .none;
836 };
837
838 // We're very likely to need space for a new entry - reserve it now to avoid
839 // the need for error cleanup logic.
840 if (ip.free_dep_entries.items.len == 0) {
841 try ip.dep_entries.ensureUnusedCapacity(gpa, 1);
842 }
843
844 // This block should allocate an entry and prepend it to the relevant `*_deps` list.
845 // The `next` field should be correctly initialized; all other fields may be undefined.
846 const new_index: DepEntry.Index = switch (dependee) {
847 .memoized_state => |stage| new_index: {
848 const deps = switch (stage) {
849 .main => &ip.memoized_state_main_deps,
850 .panic => &ip.memoized_state_panic_deps,
851 .va_list => &ip.memoized_state_va_list_deps,
852 .assembly => &ip.memoized_state_assembly_deps,
853 };
854
855 if (deps.unwrap()) |first| {
856 if (ip.dep_entries.items[@backingInt(first)].depender == .none) {
857 // Dummy entry, so we can reuse it rather than allocating a new one!
858 break :new_index first;
859 }
860 }
861
862 // Prepend a new dependency.
863 const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.pop()) |new_index| new: {
864 break :new .{ new_index, &ip.dep_entries.items[@backingInt(new_index)] };
865 } else .{ @fromBackingInt(@intCast(ip.dep_entries.items.len)), ip.dep_entries.addOneAssumeCapacity() };
866 if (deps.unwrap()) |old_first| {
867 ptr.next = old_first.toOptional();
868 ip.dep_entries.items[@backingInt(old_first)].prev = new_index.toOptional();
869 } else {
870 ptr.next = .none;
871 }
872 deps.* = new_index.toOptional();
873 break :new_index new_index;
874 },
875 inline else => |dependee_payload, tag| new_index: {
876 const gop = try switch (tag) {
877 .src_hash => ip.src_hash_deps,
878 .nav_val => ip.nav_val_deps,
879 .nav_ty => ip.nav_ty_deps,
880 .func_ies => ip.func_ies_deps,
881 .type_layout => ip.type_layout_deps,
882 .struct_defaults => ip.struct_defaults_deps,
883 .source_file => ip.source_file_deps,
884 .embed_file => ip.embed_file_deps,
885 .namespace => ip.namespace_deps,
886 .namespace_name => ip.namespace_name_deps,
887 .memoized_state => comptime unreachable,
888 }.getOrPut(gpa, dependee_payload);
889
890 if (gop.found_existing and ip.dep_entries.items[@backingInt(gop.value_ptr.*)].depender == .none) {
891 // Dummy entry, so we can reuse it rather than allocating a new one!
892 break :new_index gop.value_ptr.*;
893 }
894
895 // Prepend a new dependency.
896 const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.pop()) |new_index| new: {
897 break :new .{ new_index, &ip.dep_entries.items[@backingInt(new_index)] };
898 } else .{ @fromBackingInt(@intCast(ip.dep_entries.items.len)), ip.dep_entries.addOneAssumeCapacity() };
899 if (gop.found_existing) {
900 ptr.next = gop.value_ptr.*.toOptional();
901 ip.dep_entries.items[@backingInt(gop.value_ptr.*)].prev = new_index.toOptional();
902 } else {
903 ptr.next = .none;
904 }
905 gop.value_ptr.* = new_index;
906 break :new_index new_index;
907 },
908 };
909
910 ip.dep_entries.items[@backingInt(new_index)].depender = depender.toOptional();
911 ip.dep_entries.items[@backingInt(new_index)].prev = .none;
912 ip.dep_entries.items[@backingInt(new_index)].next_dependee = first_depender_dep;
913 ip.first_dependency.putAssumeCapacity(depender, new_index);
914}
915
916/// String is the name whose existence the dependency is on.
917/// DepEntry.Index refers to the first such dependency.
918pub const NamespaceNameKey = struct {
919 /// The instruction (`struct_decl` etc) which owns the namespace in question.
920 namespace: TrackedInst.Index,
921 /// The name whose existence the dependency is on.
922 name: NullTerminatedString,
923};
924
925pub const DepEntry = extern struct {
926 /// If null, this is a dummy entry. `next_dependee` is undefined. This is the first
927 /// entry in one of `*_deps`, and does not appear in any list by `first_dependency`,
928 /// but is not in `free_dep_entries` since `*_deps` stores a reference to it.
929 depender: AnalUnit.Optional,
930 /// Index into `dep_entries` forming a doubly linked list of all dependencies on this dependee.
931 /// Used to iterate all dependers for a given dependee during an update.
932 /// null if this is the end of the list.
933 next: DepEntry.Index.Optional,
934 /// The other link for `next`.
935 /// null if this is the start of the list.
936 prev: DepEntry.Index.Optional,
937 /// Index into `dep_entries` forming a singly linked list of dependencies *of* `depender`.
938 /// Used to efficiently remove all `DepEntry`s for a single `depender` when it is re-analyzed.
939 /// null if this is the end of the list.
940 next_dependee: DepEntry.Index.Optional,
941
942 pub const Index = enum(u32) {
943 _,
944 pub fn toOptional(dep: DepEntry.Index) Optional {
945 return @fromBackingInt(@intCast(@backingInt(dep)));
946 }
947 pub const Optional = enum(u32) {
948 none = std.math.maxInt(u32),
949 _,
950 pub fn unwrap(opt: Optional) ?DepEntry.Index {
951 return switch (opt) {
952 .none => null,
953 _ => @fromBackingInt(@intCast(@backingInt(opt))),
954 };
955 }
956 };
957 };
958};
959
960const Local = struct {
961 /// These fields can be accessed from any thread by calling `acquire`.
962 /// They are only modified by the owning thread.
963 shared: Shared align(std.atomic.cache_line),
964 /// This state is fully local to the owning thread and does not require any
965 /// atomic access.
966 mutate: struct {
967 /// When we need to allocate any long-lived buffer for mutating the `InternPool`, it is
968 /// allocated into this `arena` (for the `Id` of the thread performing the mutation). An
969 /// arena is used to avoid contention on the GPA, and to ensure that any code which retains
970 /// references to old state remains valid. For instance, when reallocing hashmap metadata,
971 /// a racing lookup on another thread may still retain a handle to the old metadata pointer,
972 /// so it must remain valid.
973 /// This arena's lifetime is tied to that of `Compilation`, although it can be cleared on
974 /// garbage collection (currently vaporware).
975 arena: std.heap.ArenaAllocator.State,
976
977 items: ListMutate,
978 extra: ListMutate,
979 limbs: ListMutate,
980 strings: ListMutate,
981 string_bytes: ListMutate,
982 tracked_insts: ListMutate,
983 files: ListMutate,
984 maps: ListMutate,
985 navs: ListMutate,
986 comptime_units: ListMutate,
987
988 namespaces: BucketListMutate,
989 } align(std.atomic.cache_line),
990
991 const Shared = struct {
992 items: List(Item),
993 extra: Extra,
994 limbs: Limbs,
995 strings: Strings,
996 string_bytes: StringBytes,
997 tracked_insts: TrackedInsts,
998 files: List(File),
999 maps: Maps,
1000 navs: Navs,
1001 comptime_units: ComptimeUnits,
1002
1003 namespaces: Namespaces,
1004
1005 pub fn getLimbs(shared: *const Local.Shared) Limbs {
1006 return switch (@sizeOf(Limb)) {
1007 @sizeOf(u32) => shared.extra,
1008 @sizeOf(u64) => shared.limbs,
1009 else => @compileError("unsupported host"),
1010 }.acquire();
1011 }
1012 };
1013
1014 const Extra = List(struct { u32 });
1015 const Limbs = switch (@sizeOf(Limb)) {
1016 @sizeOf(u32) => Extra,
1017 @sizeOf(u64) => List(struct { u64 }),
1018 else => @compileError("unsupported host"),
1019 };
1020 const Strings = List(struct { u32 });
1021 const StringBytes = List(struct { u8 });
1022 const TrackedInsts = List(struct { TrackedInst.MaybeLost });
1023 const Maps = List(struct { FieldMap });
1024 const Navs = List(Nav.Repr);
1025 const ComptimeUnits = List(struct { ComptimeUnit });
1026
1027 const namespaces_bucket_width = 8;
1028 const namespaces_bucket_mask = (1 << namespaces_bucket_width) - 1;
1029 const namespace_next_free_field = "owner_type";
1030 const Namespaces = List(struct { *[1 << namespaces_bucket_width]Zcu.Namespace });
1031
1032 const ListMutate = struct {
1033 mutex: Io.Mutex,
1034 len: u32,
1035
1036 const empty: ListMutate = .{
1037 .mutex = .init,
1038 .len = 0,
1039 };
1040 };
1041
1042 const BucketListMutate = struct {
1043 last_bucket_len: u32,
1044 buckets_list: ListMutate,
1045 free_list: u32,
1046
1047 const free_list_sentinel = std.math.maxInt(u32);
1048
1049 const empty: BucketListMutate = .{
1050 .last_bucket_len = 0,
1051 .buckets_list = ListMutate.empty,
1052 .free_list = free_list_sentinel,
1053 };
1054 };
1055
1056 fn List(comptime Elem: type) type {
1057 assert(@typeInfo(Elem) == .@"struct");
1058 return struct {
1059 bytes: [*]align(@alignOf(Elem)) u8,
1060
1061 const ListSelf = @This();
1062 const Mutable = struct {
1063 gpa: Allocator,
1064 io: Io,
1065 arena: *std.heap.ArenaAllocator.State,
1066 mutate: *ListMutate,
1067 list: *ListSelf,
1068
1069 const fields = std.enums.values(std.meta.FieldEnum(Elem));
1070
1071 fn PtrArrayElem(comptime len: usize) type {
1072 const elem_info = @typeInfo(Elem).@"struct";
1073
1074 var new_types: [elem_info.field_types.len]type = undefined;
1075 for (&new_types, elem_info.field_types) |*NewType, elem_field_type| {
1076 NewType.* = *[len]elem_field_type;
1077 }
1078 if (elem_info.is_tuple) {
1079 return @Tuple(&new_types);
1080 } else {
1081 return @Struct(.auto, null, elem_info.field_names, &new_types, &@splat(.{}));
1082 }
1083 }
1084 fn PtrElem(comptime opts: struct {
1085 size: std.lang.Type.Pointer.Size,
1086 is_const: bool = false,
1087 }) type {
1088 const elem_info = @typeInfo(Elem).@"struct";
1089 var new_types: [elem_info.field_types.len]type = undefined;
1090 for (&new_types, elem_info.field_types) |*NewType, elem_field_type| {
1091 NewType.* = @Pointer(opts.size, .{ .@"const" = opts.is_const }, elem_field_type, null);
1092 }
1093 if (elem_info.is_tuple) {
1094 return @Tuple(&new_types);
1095 } else {
1096 return @Struct(.auto, null, elem_info.field_names, &new_types, &@splat(.{}));
1097 }
1098 }
1099
1100 pub fn addOne(mutable: Mutable) Allocator.Error!PtrElem(.{ .size = .one }) {
1101 try mutable.ensureUnusedCapacity(1);
1102 return mutable.addOneAssumeCapacity();
1103 }
1104
1105 pub fn addOneAssumeCapacity(mutable: Mutable) PtrElem(.{ .size = .one }) {
1106 const index = mutable.mutate.len;
1107 assert(index < mutable.list.header().capacity);
1108 mutable.mutate.len = index + 1;
1109 const mutable_view = mutable.view().slice();
1110 var ptr: PtrElem(.{ .size = .one }) = undefined;
1111 inline for (fields) |field| {
1112 @field(ptr, @tagName(field)) = &mutable_view.items(field)[index];
1113 }
1114 return ptr;
1115 }
1116
1117 pub fn append(mutable: Mutable, elem: Elem) Allocator.Error!void {
1118 try mutable.ensureUnusedCapacity(1);
1119 mutable.appendAssumeCapacity(elem);
1120 }
1121
1122 pub fn appendAssumeCapacity(mutable: Mutable, elem: Elem) void {
1123 var mutable_view = mutable.view();
1124 defer mutable.mutate.len = @intCast(mutable_view.len);
1125 mutable_view.appendAssumeCapacity(elem);
1126 }
1127
1128 pub fn appendSliceAssumeCapacity(
1129 mutable: Mutable,
1130 slice: PtrElem(.{ .size = .slice, .is_const = true }),
1131 ) void {
1132 if (fields.len == 0) return;
1133 const start = mutable.mutate.len;
1134 const slice_len = @field(slice, @tagName(fields[0])).len;
1135 assert(slice_len <= mutable.list.header().capacity - start);
1136 mutable.mutate.len = @intCast(start + slice_len);
1137 const mutable_view = mutable.view().slice();
1138 inline for (fields) |field| {
1139 const field_slice = @field(slice, @tagName(field));
1140 assert(field_slice.len == slice_len);
1141 @memcpy(mutable_view.items(field)[start..][0..slice_len], field_slice);
1142 }
1143 }
1144
1145 pub fn appendNTimes(mutable: Mutable, elem: Elem, len: usize) Allocator.Error!void {
1146 try mutable.ensureUnusedCapacity(len);
1147 mutable.appendNTimesAssumeCapacity(elem, len);
1148 }
1149
1150 pub fn appendNTimesAssumeCapacity(mutable: Mutable, elem: Elem, len: usize) void {
1151 const start = mutable.mutate.len;
1152 assert(len <= mutable.list.header().capacity - start);
1153 mutable.mutate.len = @intCast(start + len);
1154 const mutable_view = mutable.view().slice();
1155 inline for (fields) |field| {
1156 @memset(mutable_view.items(field)[start..][0..len], @field(elem, @tagName(field)));
1157 }
1158 }
1159
1160 pub fn addManyAsArray(mutable: Mutable, comptime len: usize) Allocator.Error!PtrArrayElem(len) {
1161 try mutable.ensureUnusedCapacity(len);
1162 return mutable.addManyAsArrayAssumeCapacity(len);
1163 }
1164
1165 pub fn addManyAsArrayAssumeCapacity(mutable: Mutable, comptime len: usize) PtrArrayElem(len) {
1166 const start = mutable.mutate.len;
1167 assert(len <= mutable.list.header().capacity - start);
1168 mutable.mutate.len = @intCast(start + len);
1169 const mutable_view = mutable.view().slice();
1170 var ptr_array: PtrArrayElem(len) = undefined;
1171 inline for (fields) |field| {
1172 @field(ptr_array, @tagName(field)) = mutable_view.items(field)[start..][0..len];
1173 }
1174 return ptr_array;
1175 }
1176
1177 pub fn addManyAsSlice(mutable: Mutable, len: usize) Allocator.Error!PtrElem(.{ .size = .slice }) {
1178 try mutable.ensureUnusedCapacity(len);
1179 return mutable.addManyAsSliceAssumeCapacity(len);
1180 }
1181
1182 pub fn addManyAsSliceAssumeCapacity(mutable: Mutable, len: usize) PtrElem(.{ .size = .slice }) {
1183 const start = mutable.mutate.len;
1184 assert(len <= mutable.list.header().capacity - start);
1185 mutable.mutate.len = @intCast(start + len);
1186 const mutable_view = mutable.view().slice();
1187 var slice: PtrElem(.{ .size = .slice }) = undefined;
1188 inline for (fields) |field| {
1189 @field(slice, @tagName(field)) = mutable_view.items(field)[start..][0..len];
1190 }
1191 return slice;
1192 }
1193
1194 pub fn shrinkRetainingCapacity(mutable: Mutable, len: usize) void {
1195 assert(len <= mutable.mutate.len);
1196 mutable.mutate.len = @intCast(len);
1197 }
1198
1199 pub fn ensureUnusedCapacity(mutable: Mutable, unused_capacity: usize) Allocator.Error!void {
1200 try mutable.ensureTotalCapacity(@intCast(mutable.mutate.len + unused_capacity));
1201 }
1202
1203 pub fn ensureTotalCapacity(mutable: Mutable, total_capacity: usize) Allocator.Error!void {
1204 const old_capacity = mutable.list.header().capacity;
1205 if (old_capacity >= total_capacity) return;
1206 var new_capacity = old_capacity;
1207 while (new_capacity < total_capacity) new_capacity = (new_capacity + 10) * 2;
1208 try mutable.setCapacity(new_capacity);
1209 }
1210
1211 fn setCapacity(mutable: Mutable, capacity: u32) Allocator.Error!void {
1212 const io = mutable.io;
1213 var arena = mutable.arena.promote(mutable.gpa);
1214 defer mutable.arena.* = arena.state;
1215 const buf = try arena.allocator().alignedAlloc(
1216 u8,
1217 .fromByteUnits(alignment),
1218 bytes_offset + View.capacityInBytes(capacity),
1219 );
1220 var new_list: ListSelf = .{ .bytes = @ptrCast(buf[bytes_offset..].ptr) };
1221 new_list.header().* = .{ .capacity = capacity };
1222 const len = mutable.mutate.len;
1223 // this cold, quickly predictable, condition enables
1224 // the `MultiArrayList` optimization in `view`
1225 if (len > 0) {
1226 const old_slice = mutable.list.view().slice();
1227 const new_slice = new_list.view().slice();
1228 inline for (fields) |field| @memcpy(new_slice.items(field)[0..len], old_slice.items(field)[0..len]);
1229 }
1230 mutable.mutate.mutex.lockUncancelable(io);
1231 defer mutable.mutate.mutex.unlock(io);
1232 mutable.list.release(new_list);
1233 }
1234
1235 pub fn viewAllowEmpty(mutable: Mutable) View {
1236 const capacity = mutable.list.header().capacity;
1237 return .{
1238 .bytes = mutable.list.bytes,
1239 .len = mutable.mutate.len,
1240 .capacity = capacity,
1241 };
1242 }
1243 pub fn view(mutable: Mutable) View {
1244 const capacity = mutable.list.header().capacity;
1245 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`
1246 return .{
1247 .bytes = mutable.list.bytes,
1248 .len = mutable.mutate.len,
1249 .capacity = capacity,
1250 };
1251 }
1252 };
1253
1254 const empty: ListSelf = .{ .bytes = @constCast(&(extern struct {
1255 header: Header,
1256 bytes: [0]u8 align(@alignOf(Elem)),
1257 }{
1258 .header = .{ .capacity = 0 },
1259 .bytes = .{},
1260 }).bytes) };
1261
1262 const alignment = @max(@alignOf(Header), @alignOf(Elem));
1263 const bytes_offset = std.mem.alignForward(usize, @sizeOf(Header), @alignOf(Elem));
1264 const View = std.MultiArrayList(Elem);
1265
1266 /// Must be called when accessing from another thread.
1267 pub fn acquire(list: *const ListSelf) ListSelf {
1268 return .{ .bytes = @atomicLoad([*]align(@alignOf(Elem)) u8, &list.bytes, .acquire) };
1269 }
1270 fn release(list: *ListSelf, new_list: ListSelf) void {
1271 @atomicStore([*]align(@alignOf(Elem)) u8, &list.bytes, new_list.bytes, .release);
1272 }
1273
1274 const Header = extern struct {
1275 capacity: u32,
1276 };
1277 fn header(list: ListSelf) *Header {
1278 return @ptrCast(@alignCast(list.bytes - bytes_offset));
1279 }
1280 pub fn view(list: ListSelf) View {
1281 const capacity = list.header().capacity;
1282 assert(capacity > 0); // optimizes `MultiArrayList.Slice.items`
1283 return .{
1284 .bytes = list.bytes,
1285 .len = capacity,
1286 .capacity = capacity,
1287 };
1288 }
1289 };
1290 }
1291
1292 pub fn getMutableItems(local: *Local, gpa: Allocator, io: Io) List(Item).Mutable {
1293 return .{
1294 .gpa = gpa,
1295 .io = io,
1296 .arena = &local.mutate.arena,
1297 .mutate = &local.mutate.items,
1298 .list = &local.shared.items,
1299 };
1300 }
1301
1302 pub fn getMutableExtra(local: *Local, gpa: Allocator, io: Io) Extra.Mutable {
1303 return .{
1304 .gpa = gpa,
1305 .io = io,
1306 .arena = &local.mutate.arena,
1307 .mutate = &local.mutate.extra,
1308 .list = &local.shared.extra,
1309 };
1310 }
1311
1312 /// On 32-bit systems, this array is ignored and extra is used for everything.
1313 /// On 64-bit systems, this array is used for big integers and associated metadata.
1314 /// Use the helper methods instead of accessing this directly in order to not
1315 /// violate the above mechanism.
1316 pub fn getMutableLimbs(local: *Local, gpa: Allocator, io: Io) Limbs.Mutable {
1317 return switch (@sizeOf(Limb)) {
1318 @sizeOf(u32) => local.getMutableExtra(gpa, io),
1319 @sizeOf(u64) => .{
1320 .gpa = gpa,
1321 .io = io,
1322 .arena = &local.mutate.arena,
1323 .mutate = &local.mutate.limbs,
1324 .list = &local.shared.limbs,
1325 },
1326 else => @compileError("unsupported host"),
1327 };
1328 }
1329
1330 /// A list of offsets into `string_bytes` for each string.
1331 pub fn getMutableStrings(local: *Local, gpa: Allocator, io: Io) Strings.Mutable {
1332 return .{
1333 .gpa = gpa,
1334 .io = io,
1335 .arena = &local.mutate.arena,
1336 .mutate = &local.mutate.strings,
1337 .list = &local.shared.strings,
1338 };
1339 }
1340
1341 /// In order to store references to strings in fewer bytes, we copy all
1342 /// string bytes into here. String bytes can be null. It is up to whomever
1343 /// is referencing the data here whether they want to store both index and length,
1344 /// thus allowing null bytes, or store only index, and use null-termination. The
1345 /// `strings_bytes` array is agnostic to either usage.
1346 pub fn getMutableStringBytes(local: *Local, gpa: Allocator, io: Io) StringBytes.Mutable {
1347 return .{
1348 .gpa = gpa,
1349 .io = io,
1350 .arena = &local.mutate.arena,
1351 .mutate = &local.mutate.string_bytes,
1352 .list = &local.shared.string_bytes,
1353 };
1354 }
1355
1356 /// An index into `tracked_insts` gives a reference to a single ZIR instruction which
1357 /// persists across incremental updates.
1358 pub fn getMutableTrackedInsts(local: *Local, gpa: Allocator, io: Io) TrackedInsts.Mutable {
1359 return .{
1360 .gpa = gpa,
1361 .io = io,
1362 .arena = &local.mutate.arena,
1363 .mutate = &local.mutate.tracked_insts,
1364 .list = &local.shared.tracked_insts,
1365 };
1366 }
1367
1368 /// Elements are ordered identically to the `import_table` field of `Zcu`.
1369 ///
1370 /// Unlike `import_table`, this data is serialized as part of incremental
1371 /// compilation state.
1372 ///
1373 /// Key is the hash of the path to this file, used to store
1374 /// `InternPool.TrackedInst`.
1375 pub fn getMutableFiles(local: *Local, gpa: Allocator, io: Io) List(File).Mutable {
1376 return .{
1377 .gpa = gpa,
1378 .io = io,
1379 .arena = &local.mutate.arena,
1380 .mutate = &local.mutate.files,
1381 .list = &local.shared.files,
1382 };
1383 }
1384
1385 /// Some types such as enums, structs, and unions need to store mappings from field names
1386 /// to field index, or value to field index. In such cases, they will store the underlying
1387 /// field names and values directly, relying on one of these maps, stored separately,
1388 /// to provide lookup.
1389 /// These are not serialized; it is computed upon deserialization.
1390 pub fn getMutableMaps(local: *Local, gpa: Allocator, io: Io) Maps.Mutable {
1391 return .{
1392 .gpa = gpa,
1393 .io = io,
1394 .arena = &local.mutate.arena,
1395 .mutate = &local.mutate.maps,
1396 .list = &local.shared.maps,
1397 };
1398 }
1399
1400 pub fn getMutableNavs(local: *Local, gpa: Allocator, io: Io) Navs.Mutable {
1401 return .{
1402 .gpa = gpa,
1403 .io = io,
1404 .arena = &local.mutate.arena,
1405 .mutate = &local.mutate.navs,
1406 .list = &local.shared.navs,
1407 };
1408 }
1409
1410 pub fn getMutableComptimeUnits(local: *Local, gpa: Allocator, io: Io) ComptimeUnits.Mutable {
1411 return .{
1412 .gpa = gpa,
1413 .io = io,
1414 .arena = &local.mutate.arena,
1415 .mutate = &local.mutate.comptime_units,
1416 .list = &local.shared.comptime_units,
1417 };
1418 }
1419
1420 /// Rather than allocating Namespace objects with an Allocator, we instead allocate
1421 /// them with this BucketList. This provides four advantages:
1422 /// * Stable memory so that one thread can access a Namespace object while another
1423 /// thread allocates additional Namespace objects from this list.
1424 /// * It allows us to use u32 indexes to reference Namespace objects rather than
1425 /// pointers, saving memory in types.
1426 /// * Using integers to reference Namespace objects rather than pointers makes
1427 /// serialization trivial.
1428 /// * It provides a unique integer to be used for anonymous symbol names, avoiding
1429 /// multi-threaded contention on an atomic counter.
1430 pub fn getMutableNamespaces(local: *Local, gpa: Allocator, io: Io) Namespaces.Mutable {
1431 return .{
1432 .gpa = gpa,
1433 .io = io,
1434 .arena = &local.mutate.arena,
1435 .mutate = &local.mutate.namespaces.buckets_list,
1436 .list = &local.shared.namespaces,
1437 };
1438 }
1439};
1440
1441pub fn getLocal(ip: *InternPool, tid: Zcu.PerThread.Id) *Local {
1442 return &ip.locals[@backingInt(tid)];
1443}
1444
1445pub fn getLocalShared(ip: *const InternPool, tid: Zcu.PerThread.Id) *const Local.Shared {
1446 return &ip.locals[@backingInt(tid)].shared;
1447}
1448
1449const Shard = struct {
1450 shared: struct {
1451 map: Map(Index),
1452 string_map: Map(OptionalNullTerminatedString),
1453 tracked_inst_map: Map(TrackedInst.Index.Optional),
1454 } align(std.atomic.cache_line),
1455 mutate: struct {
1456 // TODO: measure cost of sharing unrelated mutate state
1457 map: Mutate align(std.atomic.cache_line),
1458 string_map: Mutate align(std.atomic.cache_line),
1459 tracked_inst_map: Mutate align(std.atomic.cache_line),
1460 },
1461
1462 const Mutate = struct {
1463 /// This mutex needs to be recursive because `getFuncDeclIes` interns multiple things at
1464 /// once (the function, its IES, the corresponding error union, and the resulting function
1465 /// type), so calls `getOrPutKeyEnsuringAdditionalCapacity` multiple times. Each of these
1466 /// calls acquires a lock which will only be released when the whole operation is finalized,
1467 /// and these different items could be in the same shard, in which case that shard's lock
1468 /// will be acquired multiple times.
1469 mutex: RecursiveMutex,
1470 len: u32,
1471
1472 const RecursiveMutex = struct {
1473 const OptionalTid = if (single_threaded) enum(u8) {
1474 null,
1475 main,
1476 fn unwrap(ot: OptionalTid) ?Zcu.PerThread.Id {
1477 return switch (ot) {
1478 .null => null,
1479 .main => .main,
1480 };
1481 }
1482 fn wrap(tid: Zcu.PerThread.Id) OptionalTid {
1483 comptime assert(tid == .main);
1484 return .main;
1485 }
1486 } else packed struct(u8) {
1487 non_null: bool,
1488 value: Zcu.PerThread.Id,
1489 const @"null": OptionalTid = .{ .non_null = false, .value = .main };
1490 fn unwrap(ot: OptionalTid) ?Zcu.PerThread.Id {
1491 return if (ot.non_null) ot.value else null;
1492 }
1493 fn wrap(tid: Zcu.PerThread.Id) OptionalTid {
1494 return .{ .non_null = true, .value = tid };
1495 }
1496 };
1497 mutex: Io.Mutex,
1498 tid: std.atomic.Value(OptionalTid),
1499 lock_count: u32,
1500 const init: RecursiveMutex = .{ .mutex = .init, .tid = .init(.null), .lock_count = 0 };
1501 fn lock(r: *RecursiveMutex, io: Io, tid: Zcu.PerThread.Id) void {
1502 if (r.tid.load(.monotonic) != OptionalTid.wrap(tid)) {
1503 r.mutex.lockUncancelable(io);
1504 assert(r.lock_count == 0);
1505 r.tid.store(.wrap(tid), .monotonic);
1506 }
1507 r.lock_count += 1;
1508 }
1509 fn unlock(r: *RecursiveMutex, io: Io) void {
1510 r.lock_count -= 1;
1511 if (r.lock_count == 0) {
1512 r.tid.store(.null, .monotonic);
1513 r.mutex.unlock(io);
1514 }
1515 }
1516 };
1517
1518 const empty: Mutate = .{
1519 .mutex = .init,
1520 .len = 0,
1521 };
1522 };
1523
1524 fn Map(comptime Value: type) type {
1525 comptime assert(@typeInfo(Value).@"enum".tag_type == u32);
1526 _ = @as(Value, .none); // expected .none key
1527 return struct {
1528 /// header: Header,
1529 /// entries: [header.capacity]Entry,
1530 entries: [*]Entry,
1531
1532 const empty: @This() = .{ .entries = @constCast(&(extern struct {
1533 header: Header,
1534 entries: [1]Entry,
1535 }{
1536 .header = .{ .capacity = 1 },
1537 .entries = .{.{ .value = .none, .hash = undefined }},
1538 }).entries) };
1539
1540 const alignment = @max(@alignOf(Header), @alignOf(Entry));
1541 const entries_offset = std.mem.alignForward(usize, @sizeOf(Header), @alignOf(Entry));
1542
1543 /// Must be called unless the mutate mutex is locked.
1544 fn acquire(map: *const @This()) @This() {
1545 return .{ .entries = @atomicLoad([*]Entry, &map.entries, .acquire) };
1546 }
1547 fn release(map: *@This(), new_map: @This()) void {
1548 @atomicStore([*]Entry, &map.entries, new_map.entries, .release);
1549 }
1550
1551 const Header = extern struct {
1552 capacity: u32,
1553
1554 fn mask(head: *const Header) u32 {
1555 assert(std.math.isPowerOfTwo(head.capacity));
1556 return head.capacity - 1;
1557 }
1558 };
1559 fn header(map: @This()) *Header {
1560 return @ptrCast(@alignCast(@as([*]u8, @ptrCast(map.entries)) - entries_offset));
1561 }
1562
1563 const Entry = extern struct {
1564 value: Value,
1565 hash: u32,
1566
1567 fn acquire(entry: *const Entry) Value {
1568 return @atomicLoad(Value, &entry.value, .acquire);
1569 }
1570 fn release(entry: *Entry, value: Value) void {
1571 assert(value != .none);
1572 @atomicStore(Value, &entry.value, value, .release);
1573 }
1574 fn resetUnordered(entry: *Entry) void {
1575 @atomicStore(Value, &entry.value, .none, .unordered);
1576 }
1577 };
1578 };
1579 }
1580};
1581
1582fn getTidMask(ip: *const InternPool) u32 {
1583 return @shlExact(@as(u32, 1), ip.tid_width) - 1;
1584}
1585
1586fn getIndexMask(ip: *const InternPool, comptime BackingInt: type) u32 {
1587 return @as(u32, std.math.maxInt(BackingInt)) >> ip.tid_width;
1588}
1589
1590const FieldMap = std.array_hash_map.Custom(void, void, std.array_hash_map.AutoContext(void), false);
1591
1592/// An index into `maps` which might be `none`.
1593pub const OptionalMapIndex = enum(u32) {
1594 none = std.math.maxInt(u32),
1595 _,
1596
1597 pub fn unwrap(oi: OptionalMapIndex) ?MapIndex {
1598 if (oi == .none) return null;
1599 return @fromBackingInt(@intCast(@backingInt(oi)));
1600 }
1601};
1602
1603/// An index into `maps`.
1604pub const MapIndex = enum(u32) {
1605 _,
1606
1607 pub fn get(map_index: MapIndex, ip: *const InternPool) *FieldMap {
1608 const unwrapped_map_index = map_index.unwrap(ip);
1609 const maps = ip.getLocalShared(unwrapped_map_index.tid).maps.acquire();
1610 return &maps.view().items(.@"0")[unwrapped_map_index.index];
1611 }
1612
1613 pub fn toOptional(i: MapIndex) OptionalMapIndex {
1614 return @fromBackingInt(@intCast(@backingInt(i)));
1615 }
1616
1617 const Unwrapped = struct {
1618 tid: Zcu.PerThread.Id,
1619 index: u32,
1620
1621 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) MapIndex {
1622 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
1623 assert(unwrapped.index <= ip.getIndexMask(u32));
1624 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_32) |
1625 unwrapped.index));
1626 }
1627 };
1628 fn unwrap(map_index: MapIndex, ip: *const InternPool) Unwrapped {
1629 return .{
1630 .tid = @fromBackingInt(@intCast(@backingInt(map_index) >> ip.tid_shift_32 & ip.getTidMask())),
1631 .index = @backingInt(map_index) & ip.getIndexMask(u32),
1632 };
1633 }
1634};
1635
1636pub const ComptimeAllocIndex = enum(u32) { _ };
1637
1638pub const NamespaceIndex = enum(u32) {
1639 _,
1640
1641 const Unwrapped = struct {
1642 tid: Zcu.PerThread.Id,
1643 bucket_index: u32,
1644 index: u32,
1645
1646 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) NamespaceIndex {
1647 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
1648 assert(unwrapped.bucket_index <= ip.getIndexMask(u32) >> Local.namespaces_bucket_width);
1649 assert(unwrapped.index <= Local.namespaces_bucket_mask);
1650 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_32) |
1651 unwrapped.bucket_index << Local.namespaces_bucket_width |
1652 unwrapped.index));
1653 }
1654 };
1655 fn unwrap(namespace_index: NamespaceIndex, ip: *const InternPool) Unwrapped {
1656 const index = @backingInt(namespace_index) & ip.getIndexMask(u32);
1657 return .{
1658 .tid = @fromBackingInt(@intCast(@backingInt(namespace_index) >> ip.tid_shift_32 & ip.getTidMask())),
1659 .bucket_index = index >> Local.namespaces_bucket_width,
1660 .index = index & Local.namespaces_bucket_mask,
1661 };
1662 }
1663
1664 pub fn toOptional(i: NamespaceIndex) OptionalNamespaceIndex {
1665 return @fromBackingInt(@intCast(@backingInt(i)));
1666 }
1667};
1668
1669pub const OptionalNamespaceIndex = enum(u32) {
1670 none = std.math.maxInt(u32),
1671 _,
1672
1673 pub fn init(oi: ?NamespaceIndex) OptionalNamespaceIndex {
1674 return @fromBackingInt(@intCast(@backingInt(oi orelse return .none)));
1675 }
1676
1677 pub fn unwrap(oi: OptionalNamespaceIndex) ?NamespaceIndex {
1678 if (oi == .none) return null;
1679 return @fromBackingInt(@intCast(@backingInt(oi)));
1680 }
1681};
1682
1683pub const FileIndex = enum(u32) {
1684 _,
1685
1686 const Unwrapped = struct {
1687 tid: Zcu.PerThread.Id,
1688 index: u32,
1689
1690 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) FileIndex {
1691 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
1692 assert(unwrapped.index <= ip.getIndexMask(u32));
1693 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_32) |
1694 unwrapped.index));
1695 }
1696 };
1697 pub fn unwrap(file_index: FileIndex, ip: *const InternPool) Unwrapped {
1698 return .{
1699 .tid = @fromBackingInt(@intCast(@backingInt(file_index) >> ip.tid_shift_32 & ip.getTidMask())),
1700 .index = @backingInt(file_index) & ip.getIndexMask(u32),
1701 };
1702 }
1703 pub fn toOptional(i: FileIndex) Optional {
1704 return @fromBackingInt(@intCast(@backingInt(i)));
1705 }
1706 pub const Optional = enum(u32) {
1707 none = std.math.maxInt(u32),
1708 _,
1709 pub fn unwrap(opt: Optional) ?FileIndex {
1710 return switch (opt) {
1711 .none => null,
1712 _ => @fromBackingInt(@intCast(@backingInt(opt))),
1713 };
1714 }
1715 };
1716};
1717
1718const File = struct {
1719 bin_digest: Cache.BinDigest,
1720 file: *Zcu.File,
1721 /// `.none` means no type has been created yet.
1722 root_type: InternPool.Index,
1723};
1724
1725/// An index into `strings`.
1726pub const String = enum(u32) {
1727 /// An empty string.
1728 empty = 0,
1729 _,
1730
1731 pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 {
1732 return string.toOverlongSlice(ip)[0..@intCast(len)];
1733 }
1734
1735 pub fn at(string: String, index: u64, ip: *const InternPool) u8 {
1736 return string.toOverlongSlice(ip)[@intCast(index)];
1737 }
1738
1739 pub fn toNullTerminatedString(string: String, len: u64, ip: *const InternPool) NullTerminatedString {
1740 assert(std.mem.findScalar(u8, string.toSlice(len, ip), 0) == null);
1741 assert(string.at(len, ip) == 0);
1742 return @fromBackingInt(@intCast(@backingInt(string)));
1743 }
1744
1745 const Unwrapped = struct {
1746 tid: Zcu.PerThread.Id,
1747 index: u32,
1748
1749 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) String {
1750 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
1751 assert(unwrapped.index <= ip.getIndexMask(u32));
1752 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_32) |
1753 unwrapped.index));
1754 }
1755 };
1756 fn unwrap(string: String, ip: *const InternPool) Unwrapped {
1757 return .{
1758 .tid = @fromBackingInt(@intCast(@backingInt(string) >> ip.tid_shift_32 & ip.getTidMask())),
1759 .index = @backingInt(string) & ip.getIndexMask(u32),
1760 };
1761 }
1762
1763 fn toOverlongSlice(string: String, ip: *const InternPool) []const u8 {
1764 const unwrapped = string.unwrap(ip);
1765 const local_shared = ip.getLocalShared(unwrapped.tid);
1766 const strings = local_shared.strings.acquire().view().items(.@"0");
1767 const string_bytes = local_shared.string_bytes.acquire().view().items(.@"0");
1768 return string_bytes[strings[unwrapped.index]..];
1769 }
1770
1771 const debug_state = InternPool.debug_state;
1772};
1773
1774/// An index into `strings` which might be `none`.
1775pub const OptionalString = enum(u32) {
1776 /// This is distinct from `none` - it is a valid index that represents empty string.
1777 empty = 0,
1778 none = std.math.maxInt(u32),
1779 _,
1780
1781 pub fn unwrap(string: OptionalString) ?String {
1782 return if (string != .none) @fromBackingInt(@intCast(@backingInt(string))) else null;
1783 }
1784
1785 pub fn toSlice(string: OptionalString, len: u64, ip: *const InternPool) ?[]const u8 {
1786 return (string.unwrap() orelse return null).toSlice(len, ip);
1787 }
1788
1789 const debug_state = InternPool.debug_state;
1790};
1791
1792/// An index into `strings`.
1793pub const NullTerminatedString = enum(u32) {
1794 /// An empty string.
1795 empty = 0,
1796 _,
1797
1798 /// An array of `NullTerminatedString` existing within the `extra` array.
1799 /// This type exists to provide a struct with lifetime that is
1800 /// not invalidated when items are added to the `InternPool`.
1801 pub const Slice = struct {
1802 tid: Zcu.PerThread.Id,
1803 start: u32,
1804 len: u32,
1805
1806 pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 };
1807
1808 pub fn get(slice: Slice, ip: *const InternPool) []NullTerminatedString {
1809 const extra = ip.getLocalShared(slice.tid).extra.acquire();
1810 return @ptrCast(extra.view().items(.@"0")[slice.start..][0..slice.len]);
1811 }
1812 };
1813
1814 pub fn toString(self: NullTerminatedString) String {
1815 return @fromBackingInt(@intCast(@backingInt(self)));
1816 }
1817
1818 pub fn toOptional(self: NullTerminatedString) OptionalNullTerminatedString {
1819 return @fromBackingInt(@intCast(@backingInt(self)));
1820 }
1821
1822 pub fn toSlice(string: NullTerminatedString, ip: *const InternPool) [:0]const u8 {
1823 const unwrapped = string.toString().unwrap(ip);
1824 const local_shared = ip.getLocalShared(unwrapped.tid);
1825 const strings = local_shared.strings.acquire().view().items(.@"0");
1826 const string_bytes = local_shared.string_bytes.acquire().view().items(.@"0");
1827 return string_bytes[strings[unwrapped.index] .. strings[unwrapped.index + 1] - 1 :0];
1828 }
1829
1830 pub fn length(string: NullTerminatedString, ip: *const InternPool) u32 {
1831 const unwrapped = string.toString().unwrap(ip);
1832 const local_shared = ip.getLocalShared(unwrapped.tid);
1833 const strings = local_shared.strings.acquire().view().items(.@"0");
1834 return strings[unwrapped.index + 1] - 1 - strings[unwrapped.index];
1835 }
1836
1837 pub fn eqlSlice(string: NullTerminatedString, slice: []const u8, ip: *const InternPool) bool {
1838 const overlong_slice = string.toString().toOverlongSlice(ip);
1839 return overlong_slice.len > slice.len and
1840 std.mem.eql(u8, overlong_slice[0..slice.len], slice) and
1841 overlong_slice[slice.len] == 0;
1842 }
1843
1844 const Adapter = struct {
1845 strings: []const NullTerminatedString,
1846
1847 pub fn eql(ctx: @This(), a: NullTerminatedString, b_void: void, b_map_index: usize) bool {
1848 _ = b_void;
1849 return a == ctx.strings[b_map_index];
1850 }
1851
1852 pub fn hash(ctx: @This(), a: NullTerminatedString) u32 {
1853 _ = ctx;
1854 return std.hash.int(@backingInt(a));
1855 }
1856 };
1857
1858 /// Compare based on integer value alone, ignoring the string contents.
1859 pub fn indexLessThan(ctx: void, a: NullTerminatedString, b: NullTerminatedString) bool {
1860 _ = ctx;
1861 return @backingInt(a) < @backingInt(b);
1862 }
1863
1864 pub fn toUnsigned(string: NullTerminatedString, ip: *const InternPool) ?u32 {
1865 const slice = string.toSlice(ip);
1866 if (slice.len > 1 and slice[0] == '0') return null;
1867 if (std.mem.findScalar(u8, slice, '_')) |_| return null;
1868 return std.fmt.parseUnsigned(u32, slice, 10) catch null;
1869 }
1870
1871 const FormatData = struct {
1872 string: NullTerminatedString,
1873 ip: *const InternPool,
1874 id: bool,
1875 };
1876 fn format(data: FormatData, writer: *Io.Writer) Io.Writer.Error!void {
1877 const slice = data.string.toSlice(data.ip);
1878 if (!data.id) {
1879 try writer.writeAll(slice);
1880 } else {
1881 try writer.print("{f}", .{std.zig.fmtIdP(slice)});
1882 }
1883 }
1884
1885 pub fn fmt(string: NullTerminatedString, ip: *const InternPool) std.fmt.Alt(FormatData, format) {
1886 return .{ .data = .{ .string = string, .ip = ip, .id = false } };
1887 }
1888
1889 pub fn fmtId(string: NullTerminatedString, ip: *const InternPool) std.fmt.Alt(FormatData, format) {
1890 return .{ .data = .{ .string = string, .ip = ip, .id = true } };
1891 }
1892
1893 const debug_state = InternPool.debug_state;
1894};
1895
1896/// An index into `strings` which might be `none`.
1897pub const OptionalNullTerminatedString = enum(u32) {
1898 /// This is distinct from `none` - it is a valid index that represents empty string.
1899 empty = 0,
1900 none = std.math.maxInt(u32),
1901 _,
1902
1903 pub fn unwrap(string: OptionalNullTerminatedString) ?NullTerminatedString {
1904 return if (string != .none) @fromBackingInt(@intCast(@backingInt(string))) else null;
1905 }
1906
1907 pub fn toSlice(string: OptionalNullTerminatedString, ip: *const InternPool) ?[:0]const u8 {
1908 return (string.unwrap() orelse return null).toSlice(ip);
1909 }
1910
1911 const debug_state = InternPool.debug_state;
1912};
1913
1914/// A single value captured in the closure of a namespace type. This is not a plain
1915/// `Index` because we must differentiate between the following cases:
1916/// * runtime-known value (where we store the type)
1917/// * comptime-known value (where we store the value)
1918/// * `Nav` val (so that we can analyze the value lazily)
1919/// * `Nav` ref (so that we can analyze the reference lazily)
1920pub const CaptureValue = packed struct(u32) {
1921 tag: enum(u2) { @"comptime", runtime, nav_val, nav_ref },
1922 idx: u30,
1923
1924 pub fn wrap(val: Unwrapped) CaptureValue {
1925 return switch (val) {
1926 .@"comptime" => |i| .{ .tag = .@"comptime", .idx = @intCast(@backingInt(i)) },
1927 .runtime => |i| .{ .tag = .runtime, .idx = @intCast(@backingInt(i)) },
1928 .nav_val => |i| .{ .tag = .nav_val, .idx = @intCast(@backingInt(i)) },
1929 .nav_ref => |i| .{ .tag = .nav_ref, .idx = @intCast(@backingInt(i)) },
1930 };
1931 }
1932 pub fn unwrap(val: CaptureValue) Unwrapped {
1933 return switch (val.tag) {
1934 .@"comptime" => .{ .@"comptime" = @fromBackingInt(@intCast(val.idx)) },
1935 .runtime => .{ .runtime = @fromBackingInt(@intCast(val.idx)) },
1936 .nav_val => .{ .nav_val = @fromBackingInt(@intCast(val.idx)) },
1937 .nav_ref => .{ .nav_ref = @fromBackingInt(@intCast(val.idx)) },
1938 };
1939 }
1940
1941 pub const Unwrapped = union(enum) {
1942 /// Index refers to the value.
1943 @"comptime": Index,
1944 /// Index refers to the type.
1945 runtime: Index,
1946 nav_val: Nav.Index,
1947 nav_ref: Nav.Index,
1948 };
1949
1950 pub const Slice = struct {
1951 tid: Zcu.PerThread.Id,
1952 start: u32,
1953 len: u32,
1954
1955 pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 };
1956
1957 pub fn get(slice: Slice, ip: *const InternPool) []CaptureValue {
1958 const extra = ip.getLocalShared(slice.tid).extra.acquire();
1959 return @ptrCast(extra.view().items(.@"0")[slice.start..][0..slice.len]);
1960 }
1961 };
1962};
1963
1964pub const Key = union(enum) {
1965 int_type: IntType,
1966 ptr_type: PtrType,
1967 array_type: ArrayType,
1968 vector_type: VectorType,
1969 opt_type: Index,
1970 /// `anyframe->T`. The payload is the child type, which may be `none` to indicate
1971 /// `anyframe`.
1972 anyframe_type: Index,
1973 error_union_type: ErrorUnionType,
1974 simple_type: SimpleType,
1975 /// This represents a struct that has been explicitly declared in source code,
1976 /// or was created with `@Struct`. It is unique and based on a declaration.
1977 struct_type: ContainerType,
1978 /// This is a tuple type. Tuples are logically similar to structs, but have some
1979 /// important differences in semantics; they do not undergo staged type resolution,
1980 /// so cannot be self-referential, and they are not considered container/namespace
1981 /// types, so cannot have declarations and have structural equality properties.
1982 tuple_type: TupleType,
1983 union_type: ContainerType,
1984 opaque_type: ContainerType,
1985 enum_type: ContainerType,
1986 spirv_type: SpirvType,
1987 func_type: FuncType,
1988 error_set_type: ErrorSetType,
1989 /// The payload is the function body, either a `func_decl` or `func_instance`.
1990 inferred_error_set_type: Index,
1991
1992 /// Typed `undefined`. This will never be `none`; untyped `undefined` is represented
1993 /// via `simple_value` and has a named `Index` tag for it.
1994 undef: Index,
1995 simple_value: SimpleValue,
1996 @"extern": Extern,
1997 func: Func,
1998 int: Key.Int,
1999 err: Error,
2000 error_union: ErrorUnion,
2001 enum_literal: NullTerminatedString,
2002 /// A specific enum tag, indicated by the integer tag value.
2003 enum_tag: EnumTag,
2004 float: Float,
2005 ptr: Ptr,
2006 slice: Slice,
2007 opt: Opt,
2008 /// An instance of a struct, array, or vector.
2009 /// Each element/field stored as an `Index`.
2010 /// In the case of sentinel-terminated arrays, the sentinel value *is* stored,
2011 /// so the slice length will be one more than the type's array length.
2012 /// There must be at least one element which is not `undefined`. If all elements are
2013 /// undefined, instead create an undefined value of the aggregate type.
2014 aggregate: Aggregate,
2015 /// An instance of a union.
2016 un: Union,
2017 /// An instance of a `packed struct` or `packed union`.
2018 bitpack: Bitpack,
2019
2020 /// A comptime function call with a memoized result.
2021 memoized_call: Key.MemoizedCall,
2022
2023 pub const TypeValue = extern struct {
2024 ty: Index,
2025 val: Index,
2026 };
2027
2028 pub const IntType = std.lang.Type.Int;
2029
2030 /// Extern for hashing via memory reinterpretation.
2031 pub const ErrorUnionType = extern struct {
2032 error_set_type: Index,
2033 payload_type: Index,
2034 };
2035
2036 pub const ErrorSetType = struct {
2037 /// Set of error names, sorted by null terminated string index.
2038 names: NullTerminatedString.Slice,
2039 /// This is ignored by `get` but will always be provided by `indexToKey`.
2040 names_map: OptionalMapIndex = .none,
2041
2042 /// Look up field index based on field name.
2043 pub fn nameIndex(self: ErrorSetType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
2044 const map = self.names_map.unwrap().?.get(ip);
2045 const adapter: NullTerminatedString.Adapter = .{ .strings = self.names.get(ip) };
2046 const field_index = map.getIndexAdapted(name, adapter) orelse return null;
2047 return @intCast(field_index);
2048 }
2049 };
2050
2051 /// Extern layout so it can be hashed with `std.mem.asBytes`.
2052 pub const PtrType = extern struct {
2053 child: Index,
2054 sentinel: Index = .none,
2055 flags: Flags = .{},
2056 packed_offset: PackedOffset = .{ .bit_offset = 0, .host_size = 0 },
2057
2058 pub const VectorIndex = enum(u16) {
2059 none = std.math.maxInt(u16),
2060 _,
2061 };
2062
2063 pub const Flags = packed struct(u32) {
2064 size: Size = .one,
2065 /// `none` indicates the ABI alignment of the pointee_type. In this
2066 /// case, this field *must* be set to `none`, otherwise the
2067 /// `InternPool` equality and hashing functions will return incorrect
2068 /// results.
2069 alignment: Alignment = .none,
2070 is_const: bool = false,
2071 is_volatile: bool = false,
2072 is_allowzero: bool = false,
2073 /// See src/target.zig defaultAddressSpace function for how to obtain
2074 /// an appropriate value for this field.
2075 address_space: AddressSpace = .generic,
2076 vector_index: VectorIndex = .none,
2077 };
2078
2079 pub const PackedOffset = packed struct(u32) {
2080 /// If this is non-zero it means the pointer points to a sub-byte
2081 /// range of data, which is backed by a "host integer" with this
2082 /// number of bytes.
2083 /// When host_size=pointee_abi_size and bit_offset=0, this must be
2084 /// represented with host_size=0 instead.
2085 host_size: u16,
2086 bit_offset: u16,
2087 };
2088
2089 pub const Size = std.lang.Type.Pointer.Size;
2090 pub const AddressSpace = std.lang.AddressSpace;
2091 };
2092
2093 /// Extern so that hashing can be done via memory reinterpreting.
2094 pub const ArrayType = extern struct {
2095 len: u64,
2096 child: Index,
2097 sentinel: Index = .none,
2098
2099 pub fn lenIncludingSentinel(array_type: ArrayType) u64 {
2100 return array_type.len + @intFromBool(array_type.sentinel != .none);
2101 }
2102 };
2103
2104 /// Extern so that hashing can be done via memory reinterpreting.
2105 pub const VectorType = extern struct {
2106 len: u32,
2107 child: Index,
2108 };
2109
2110 pub const TupleType = struct {
2111 types: Index.Slice,
2112 /// These elements may be `none`, indicating runtime-known.
2113 values: Index.Slice,
2114 };
2115
2116 /// This is the hashmap key. To fetch other data associated with the type, see:
2117 /// * `loadStructType`
2118 /// * `loadUnionType`
2119 /// * `loadEnumType`
2120 /// * `loadOpaqueType`
2121 pub const ContainerType = union(enum) {
2122 /// This type corresponds to an actual source declaration, e.g. `struct { ... }`.
2123 /// It is hashed based on its ZIR instruction index and set of captures.
2124 declared: Declared,
2125 /// This type originates from a reification via `@Enum`, `@Struct`, `@Union` or from an anonymous initialization.
2126 /// It is hashed based on its ZIR instruction index and fields, attributes, etc.
2127 /// To avoid making this key overly complex, the type-specific data is hashed by Sema.
2128 reified: struct {
2129 /// A `reify`, `struct_init`, `struct_init_ref`, or `struct_init_anon` instruction.
2130 /// Alternatively, this is `main_struct_inst` of a ZON file.
2131 zir_index: TrackedInst.Index,
2132 /// A hash of this type's attributes, fields, etc, generated by Sema.
2133 type_hash: u64,
2134 },
2135 /// This type is an automatically-generated enum tag type for this union type.
2136 /// It is hashed based on the index of the union type it corresponds to.
2137 generated_union_tag: Index,
2138
2139 pub const Declared = struct {
2140 /// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction.
2141 zir_index: TrackedInst.Index,
2142 /// The captured values of this type. These values must be fully resolved per the language spec.
2143 captures: union(enum) {
2144 owned: CaptureValue.Slice,
2145 external: []const CaptureValue,
2146 },
2147 };
2148 };
2149
2150 pub const SpirvType = extern struct {
2151 /// If tag is `.image`, this is the sampled type or `.none` if `usage` is `.storage`.
2152 /// If tag is `.sampled_image`, this is the image type.
2153 /// If tag is `.runtime_array`, this is the element type.
2154 /// Otherwise this is `.none`.
2155 ty: Index,
2156 flags: Flags,
2157
2158 pub const Flags = packed struct(u32) {
2159 tag: @typeInfo(std.lang.Type.Spirv).@"union".tag_type.?,
2160 // Image type flags
2161 usage: @typeInfo(std.lang.Type.Spirv.Image.Usage).@"union".tag_type.?,
2162 format: std.lang.Type.Spirv.Image.Format,
2163 dim: std.lang.Type.Spirv.Image.Dimensionality,
2164 depth: std.lang.Type.Spirv.Image.Depth,
2165 access: std.lang.Type.Spirv.Image.Access,
2166 is_arrayed: bool,
2167 is_multisampled: bool,
2168
2169 _: u16 = 0,
2170 };
2171 };
2172
2173 pub const FuncType = struct {
2174 param_types: Index.Slice,
2175 return_type: Index,
2176 /// Tells whether a parameter is comptime. See `paramIsComptime` helper
2177 /// method for accessing this.
2178 comptime_bits: u32,
2179 /// Tells whether a parameter is noalias. See `paramIsNoalias` helper
2180 /// method for accessing this.
2181 noalias_bits: u32,
2182 cc: std.lang.CallingConvention,
2183 is_var_args: bool,
2184 is_noinline: bool,
2185
2186 pub fn paramIsComptime(self: @This(), i: u5) bool {
2187 assert(i < self.param_types.len);
2188 return @as(u1, @truncate(self.comptime_bits >> i)) != 0;
2189 }
2190
2191 pub fn paramIsNoalias(self: @This(), i: u5) bool {
2192 assert(i < self.param_types.len);
2193 return @as(u1, @truncate(self.noalias_bits >> i)) != 0;
2194 }
2195
2196 pub fn eql(a: FuncType, b: FuncType, ip: *const InternPool) bool {
2197 return std.mem.eql(Index, a.param_types.get(ip), b.param_types.get(ip)) and
2198 a.return_type == b.return_type and
2199 a.comptime_bits == b.comptime_bits and
2200 a.noalias_bits == b.noalias_bits and
2201 a.is_var_args == b.is_var_args and
2202 a.is_noinline == b.is_noinline and
2203 std.meta.eql(a.cc, b.cc);
2204 }
2205
2206 pub fn hash(self: FuncType, hasher: *Hash, ip: *const InternPool) void {
2207 for (self.param_types.get(ip)) |param_type| {
2208 std.hash.autoHash(hasher, param_type);
2209 }
2210 std.hash.autoHash(hasher, self.return_type);
2211 std.hash.autoHash(hasher, self.comptime_bits);
2212 std.hash.autoHash(hasher, self.noalias_bits);
2213 std.hash.autoHash(hasher, self.cc);
2214 std.hash.autoHash(hasher, self.is_var_args);
2215 std.hash.autoHash(hasher, self.is_noinline);
2216 }
2217 };
2218
2219 pub const Extern = struct {
2220 /// The name of the extern symbol.
2221 name: NullTerminatedString,
2222 /// The type of the extern symbol itself.
2223 /// This may be `.anyopaque_type`, in which case the value may not be loaded.
2224 ty: Index,
2225 /// Library name if specified.
2226 /// For example `extern "c" fn write(...) usize` would have 'c' as library name.
2227 /// Index into the string table bytes.
2228 lib_name: OptionalNullTerminatedString,
2229 linkage: std.lang.GlobalLinkage,
2230 visibility: std.lang.SymbolVisibility,
2231 is_threadlocal: bool,
2232 is_dll_import: bool,
2233 relocation: std.lang.ExternOptions.Relocation,
2234 decoration: ?std.lang.ExternOptions.Decoration,
2235 is_const: bool,
2236 alignment: Alignment,
2237 @"addrspace": std.lang.AddressSpace,
2238 /// The ZIR instruction which created this extern; used only for source locations.
2239 /// This is a `declaration`.
2240 zir_index: TrackedInst.Index,
2241 /// The `Nav` corresponding to this extern symbol.
2242 /// This is ignored by hashing and equality.
2243 owner_nav: Nav.Index,
2244 source: Tag.Extern.Flags.Source,
2245 };
2246
2247 pub const Func = struct {
2248 tid: Zcu.PerThread.Id,
2249 /// In the case of a generic function, this type will potentially have fewer parameters
2250 /// than the generic owner's type, because the comptime parameters will be deleted.
2251 ty: Index,
2252 /// If this is a function body that has been coerced to a different type, for example
2253 /// ```
2254 /// fn f2() !void {}
2255 /// const f: fn()anyerror!void = f2;
2256 /// ```
2257 /// then it contains the original type of the function body.
2258 uncoerced_ty: Index,
2259 /// Index into extra array of the `FuncAnalysis` corresponding to this function.
2260 /// Used for mutating that data.
2261 analysis_extra_index: u32,
2262 /// Index into extra array of the `zir_body_inst` corresponding to this function.
2263 /// Used for mutating that data.
2264 zir_body_inst_extra_index: u32,
2265 /// Index into extra array of the resolved inferred error set for this function.
2266 /// Used for mutating that data.
2267 /// 0 when the function does not have an inferred error set.
2268 resolved_error_set_extra_index: u32,
2269 /// When a generic function is instantiated, branch_quota is inherited from the
2270 /// active Sema context. Importantly, this value is also updated when an existing
2271 /// generic function instantiation is found and called.
2272 /// This field contains the index into the extra array of this value,
2273 /// so that it can be mutated.
2274 /// This will be 0 when the function is not a generic function instantiation.
2275 branch_quota_extra_index: u32,
2276 owner_nav: Nav.Index,
2277 /// The ZIR instruction that is a function instruction. Use this to find
2278 /// the body. We store this rather than the body directly so that when ZIR
2279 /// is regenerated on update(), we can map this to the new corresponding
2280 /// ZIR instruction.
2281 zir_body_inst: TrackedInst.Index,
2282 /// Relative to owner Decl.
2283 lbrace_line: u32,
2284 /// Relative to owner Decl.
2285 rbrace_line: u32,
2286 lbrace_column: u32,
2287 rbrace_column: u32,
2288
2289 /// The `func_decl` which is the generic function from whence this instance was spawned.
2290 /// If this is `none` it means the function is not a generic instantiation.
2291 generic_owner: Index,
2292 /// If this is a generic function instantiation, this will be non-empty.
2293 /// Corresponds to the parameters of the `generic_owner` type, which
2294 /// may have more parameters than `ty`.
2295 /// Each element is the comptime-known value the generic function was instantiated with,
2296 /// or `none` if the element is runtime-known.
2297 /// TODO: as a follow-up optimization, don't store `none` values here since that data
2298 /// is redundant with `comptime_bits` stored elsewhere.
2299 comptime_args: Index.Slice,
2300
2301 /// Returns a pointer that becomes invalid after any additions to the `InternPool`.
2302 fn analysisPtr(func: Func, ip: *const InternPool) *FuncAnalysis {
2303 const extra = ip.getLocalShared(func.tid).extra.acquire();
2304 return @ptrCast(&extra.view().items(.@"0")[func.analysis_extra_index]);
2305 }
2306
2307 pub fn analysisUnordered(func: Func, ip: *const InternPool) FuncAnalysis {
2308 return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered);
2309 }
2310
2311 pub fn setBranchHint(func: Func, ip: *InternPool, io: Io, hint: std.lang.BranchHint) void {
2312 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
2313 extra_mutex.lockUncancelable(io);
2314 defer extra_mutex.unlock(io);
2315
2316 const analysis_ptr = func.analysisPtr(ip);
2317 var analysis = analysis_ptr.*;
2318 analysis.branch_hint = hint;
2319 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
2320 }
2321
2322 /// Returns a pointer that becomes invalid after any additions to the `InternPool`.
2323 fn zirBodyInstPtr(func: Func, ip: *const InternPool) *TrackedInst.Index {
2324 const extra = ip.getLocalShared(func.tid).extra.acquire();
2325 return @ptrCast(&extra.view().items(.@"0")[func.zir_body_inst_extra_index]);
2326 }
2327
2328 pub fn zirBodyInstUnordered(func: Func, ip: *const InternPool) TrackedInst.Index {
2329 return @atomicLoad(TrackedInst.Index, func.zirBodyInstPtr(ip), .unordered);
2330 }
2331
2332 /// Returns a pointer that becomes invalid after any additions to the `InternPool`.
2333 fn branchQuotaPtr(func: Func, ip: *const InternPool) *u32 {
2334 const extra = ip.getLocalShared(func.tid).extra.acquire();
2335 return &extra.view().items(.@"0")[func.branch_quota_extra_index];
2336 }
2337
2338 pub fn branchQuotaUnordered(func: Func, ip: *const InternPool) u32 {
2339 return @atomicLoad(u32, func.branchQuotaPtr(ip), .unordered);
2340 }
2341
2342 pub fn maxBranchQuota(func: Func, ip: *InternPool, io: Io, new_branch_quota: u32) void {
2343 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
2344 extra_mutex.lockUncancelable(io);
2345 defer extra_mutex.unlock(io);
2346
2347 const branch_quota_ptr = func.branchQuotaPtr(ip);
2348 @atomicStore(u32, branch_quota_ptr, @max(branch_quota_ptr.*, new_branch_quota), .release);
2349 }
2350
2351 /// Returns a pointer that becomes invalid after any additions to the `InternPool`.
2352 fn resolvedErrorSetPtr(func: Func, ip: *const InternPool) *Index {
2353 const extra = ip.getLocalShared(func.tid).extra.acquire();
2354 assert(func.analysisUnordered(ip).inferred_error_set);
2355 return @ptrCast(&extra.view().items(.@"0")[func.resolved_error_set_extra_index]);
2356 }
2357
2358 pub fn resolvedErrorSetUnordered(func: Func, ip: *const InternPool) Index {
2359 return @atomicLoad(Index, func.resolvedErrorSetPtr(ip), .unordered);
2360 }
2361
2362 pub fn setResolvedErrorSet(func: Func, ip: *InternPool, io: Io, ies: Index) void {
2363 const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
2364 extra_mutex.lockUncancelable(io);
2365 defer extra_mutex.unlock(io);
2366
2367 @atomicStore(Index, func.resolvedErrorSetPtr(ip), ies, .release);
2368 }
2369 };
2370
2371 pub const Int = struct {
2372 ty: Index,
2373 storage: Storage,
2374
2375 pub const Storage = union(enum) {
2376 u64: u64,
2377 i64: i64,
2378 big_int: BigIntConst,
2379
2380 /// Big enough to fit any non-BigInt value
2381 pub const BigIntSpace = struct {
2382 /// The +1 is headroom so that operations such as incrementing once
2383 /// or decrementing once are possible without using an allocator.
2384 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
2385 };
2386
2387 pub fn toBigInt(storage: Storage, space: *BigIntSpace) BigIntConst {
2388 return switch (storage) {
2389 .big_int => |x| x,
2390 inline .u64, .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
2391 };
2392 }
2393 };
2394 };
2395
2396 pub const Error = extern struct {
2397 ty: Index,
2398 name: NullTerminatedString,
2399 };
2400
2401 pub const ErrorUnion = struct {
2402 ty: Index,
2403 val: Value,
2404
2405 pub const Value = union(enum) {
2406 err_name: NullTerminatedString,
2407 payload: Index,
2408 };
2409 };
2410
2411 pub const EnumTag = extern struct {
2412 /// The enum type.
2413 ty: Index,
2414 /// The integer tag value which has the integer tag type of the enum.
2415 int: Index,
2416 };
2417
2418 pub const Float = struct {
2419 ty: Index,
2420 /// The storage used must match the size of the float type being represented.
2421 storage: Storage,
2422
2423 pub const Storage = union(enum) {
2424 f16: f16,
2425 f32: f32,
2426 f64: f64,
2427 f80: f80,
2428 f128: f128,
2429 };
2430 };
2431
2432 pub const Ptr = struct {
2433 /// This is the pointer type, not the element type.
2434 ty: Index,
2435 /// The base address which this pointer is offset from.
2436 base_addr: BaseAddr,
2437 /// The offset of this pointer from `base_addr` in bytes.
2438 byte_offset: u64,
2439
2440 pub const BaseAddr = union(enum) {
2441 const Tag = @typeInfo(BaseAddr).@"union".tag_type.?;
2442
2443 /// Points to the value of a single `Nav`.
2444 nav: Nav.Index,
2445
2446 /// Points to the value of a single comptime alloc stored in `Sema`.
2447 comptime_alloc: ComptimeAllocIndex,
2448
2449 /// Points to a single unnamed constant value.
2450 uav: Uav,
2451
2452 /// Points to a comptime field of a struct. Index is the field's value.
2453 ///
2454 /// TODO: this exists because these fields are semantically mutable. We
2455 /// should probably change the language so that this isn't the case.
2456 comptime_field: Index,
2457
2458 /// A pointer with a fixed integer address, usually from `@ptrFromInt`.
2459 ///
2460 /// The address is stored entirely by `byte_offset`, which will be positive
2461 /// and in-range of a `usize`. The base address is, for all intents and purposes, 0.
2462 int,
2463
2464 /// A pointer to the payload of an error union. Index is the error union pointer.
2465 /// To ensure a canonical representation, the type of the base pointer must:
2466 /// * be a one-pointer
2467 /// * be `const`, `volatile` and `allowzero`
2468 /// * have alignment 1
2469 /// * have the same address space as this pointer
2470 /// * have a host size, bit offset, and vector index of 0
2471 /// See `Value.canonicalizeBasePtr` which enforces these properties.
2472 eu_payload: Index,
2473
2474 /// A pointer to the payload of a non-pointer-like optional. Index is the
2475 /// optional pointer. To ensure a canonical representation, the base
2476 /// pointer is subject to the same restrictions as in `eu_payload`.
2477 opt_payload: Index,
2478
2479 /// A pointer to a field of a slice, or of an auto-layout struct or union. Slice fields
2480 /// are referenced according to `Value.slice_ptr_index` and `Value.slice_len_index`.
2481 /// Base is the aggregate pointer, which is subject to the same restrictions as
2482 /// in `eu_payload`.
2483 field: BaseIndex,
2484
2485 /// A pointer to an element of a comptime-only array. Base is the
2486 /// many-pointer we are indexing into. It is subject to the same restrictions
2487 /// as in `eu_payload`, except it must be a many-pointer rather than a one-pointer.
2488 ///
2489 /// The element type of the base pointer must NOT be an array. Additionally, the
2490 /// base pointer is guaranteed to not be an `arr_elem` into a pointer with the
2491 /// same child type. Thus, since there are no two comptime-only types which are
2492 /// IMC to one another, the only case where the base pointer may also be an
2493 /// `arr_elem` is when this pointer is semantically invalid (e.g. it reinterprets
2494 /// a `type` as a `comptime_int`). These restrictions are in place to ensure
2495 /// a canonical representation.
2496 ///
2497 /// This kind of base address differs from others in that it may refer to any
2498 /// sequence of values; for instance, an `arr_elem` at index 2 may refer to
2499 /// any number of elements starting from index 2.
2500 ///
2501 /// Index must not be 0. To refer to the element at index 0, simply reinterpret
2502 /// the aggregate pointer.
2503 arr_elem: BaseIndex,
2504
2505 pub const BaseIndex = struct {
2506 base: Index,
2507 index: u64,
2508 };
2509 pub const Uav = extern struct {
2510 val: Index,
2511 /// Contains the canonical pointer type of the anonymous
2512 /// declaration. This may equal `ty` of the `Ptr` or it may be
2513 /// different. Importantly, when lowering the anonymous decl,
2514 /// the original pointer type alignment must be used.
2515 orig_ty: Index,
2516 };
2517
2518 pub fn eql(a: BaseAddr, b: BaseAddr) bool {
2519 if (@as(Key.Ptr.BaseAddr.Tag, a) != @as(Key.Ptr.BaseAddr.Tag, b)) return false;
2520
2521 return switch (a) {
2522 .nav => |a_nav| a_nav == b.nav,
2523 .comptime_alloc => |a_alloc| a_alloc == b.comptime_alloc,
2524 .uav => |ad| ad.val == b.uav.val and
2525 ad.orig_ty == b.uav.orig_ty,
2526 .int => true,
2527 .eu_payload => |a_eu_payload| a_eu_payload == b.eu_payload,
2528 .opt_payload => |a_opt_payload| a_opt_payload == b.opt_payload,
2529 .comptime_field => |a_comptime_field| a_comptime_field == b.comptime_field,
2530 .arr_elem => |a_elem| std.meta.eql(a_elem, b.arr_elem),
2531 .field => |a_field| std.meta.eql(a_field, b.field),
2532 };
2533 }
2534 };
2535 };
2536
2537 pub const Slice = struct {
2538 /// This is the slice type, not the element type.
2539 ty: Index,
2540 /// The slice's `ptr` field. Must be a many-ptr with the same properties as `ty`.
2541 ptr: Index,
2542 /// The slice's `len` field. Must be a `usize`.
2543 len: Index,
2544 };
2545
2546 /// `null` is represented by the `val` field being `none`.
2547 pub const Opt = extern struct {
2548 /// This is the optional type; not the payload type.
2549 ty: Index,
2550 /// This could be `none`, indicating the optional is `null`.
2551 val: Index,
2552 };
2553
2554 pub const Union = extern struct {
2555 /// This is the union type; not the field type.
2556 ty: Index,
2557 /// Indicates the active field. This could be `none`, which indicates the tag is not known. `none` is only a valid value for extern and packed unions.
2558 /// In those cases, the type of `val` is:
2559 /// extern: a u8 array of the same byte length as the union
2560 /// packed: an unsigned integer with the same bit size as the union
2561 tag: Index,
2562 /// The value of the active field.
2563 val: Index,
2564 };
2565
2566 pub const Aggregate = struct {
2567 ty: Index,
2568 storage: Storage,
2569
2570 pub const Storage = union(enum) {
2571 bytes: String,
2572 elems: []const Index,
2573 repeated_elem: Index,
2574
2575 pub fn values(self: *const Storage) []const Index {
2576 return switch (self.*) {
2577 .bytes => &.{},
2578 .elems => |elems| elems,
2579 .repeated_elem => |*elem| @as(*const [1]Index, elem),
2580 };
2581 }
2582 };
2583 };
2584
2585 /// As well as a key, this type doubles as the payload in `extra` for `Tag.bitpack`.
2586 pub const Bitpack = struct {
2587 /// The `packed struct` or `packed union` type.
2588 ty: Index,
2589 /// The contents of the bitpack, represented as the backing integer value. The type of this
2590 /// value is the same as the backing integer type of `ty`.
2591 backing_int_val: Index,
2592 };
2593
2594 pub const MemoizedCall = struct {
2595 func: Index,
2596 arg_values: []const Index,
2597 result: Index,
2598 branch_count: u32,
2599 branch_quota: u32,
2600 };
2601
2602 pub fn hash32(key: Key, ip: *const InternPool) u32 {
2603 return @truncate(key.hash64(ip));
2604 }
2605
2606 pub fn hash64(key: Key, ip: *const InternPool) u64 {
2607 const asBytes = std.mem.asBytes;
2608 const KeyTag = @typeInfo(Key).@"union".tag_type.?;
2609 const seed = @backingInt(@as(KeyTag, key));
2610 return switch (key) {
2611 inline .ptr_type,
2612 .array_type,
2613 .vector_type,
2614 .opt_type,
2615 .anyframe_type,
2616 .error_union_type,
2617 .spirv_type,
2618 .simple_type,
2619 .simple_value,
2620 .opt,
2621 .undef,
2622 .err,
2623 .enum_literal,
2624 .enum_tag,
2625 .inferred_error_set_type,
2626 .un,
2627 => |x| {
2628 _ = extern struct { is_extern: @TypeOf(x) };
2629 comptime assert(std.meta.hasUniqueRepresentation(@TypeOf(x)));
2630 return Hash.hash(seed, asBytes(&x));
2631 },
2632
2633 .int_type => |x| Hash.hash(seed + @backingInt(x.signedness), asBytes(&x.bits)),
2634
2635 .error_union => |x| switch (x.val) {
2636 .err_name => |y| Hash.hash(seed + 0, asBytes(&x.ty) ++ asBytes(&y)),
2637 .payload => |y| Hash.hash(seed + 1, asBytes(&x.ty) ++ asBytes(&y)),
2638 },
2639
2640 .opaque_type,
2641 .enum_type,
2642 .union_type,
2643 .struct_type,
2644 => |namespace_type| {
2645 var hasher = Hash.init(seed);
2646 std.hash.autoHash(&hasher, std.meta.activeTag(namespace_type));
2647 switch (namespace_type) {
2648 .declared => |declared| {
2649 std.hash.autoHash(&hasher, declared.zir_index);
2650 const captures = switch (declared.captures) {
2651 .owned => |cvs| cvs.get(ip),
2652 .external => |cvs| cvs,
2653 };
2654 for (captures) |cv| {
2655 std.hash.autoHash(&hasher, cv);
2656 }
2657 },
2658 .reified => |reified| {
2659 std.hash.autoHash(&hasher, reified.zir_index);
2660 std.hash.autoHash(&hasher, reified.type_hash);
2661 },
2662 .generated_union_tag => |union_type| {
2663 std.hash.autoHash(&hasher, union_type);
2664 },
2665 }
2666 return hasher.final();
2667 },
2668
2669 .int => |int| {
2670 var hasher = Hash.init(seed);
2671 // Canonicalize all integers by converting them to BigIntConst.
2672 var buffer: Key.Int.Storage.BigIntSpace = undefined;
2673 const big_int = int.storage.toBigInt(&buffer);
2674
2675 std.hash.autoHash(&hasher, int.ty);
2676 std.hash.autoHash(&hasher, big_int.positive or big_int.eqlZero());
2677 for (big_int.limbs) |limb| std.hash.autoHash(&hasher, limb);
2678 return hasher.final();
2679 },
2680
2681 .float => |float| {
2682 var hasher = Hash.init(seed);
2683 std.hash.autoHash(&hasher, float.ty);
2684 switch (float.storage) {
2685 inline else => |val| std.hash.autoHash(
2686 &hasher,
2687 @as(@Int(.unsigned, @bitSizeOf(@TypeOf(val))), @bitCast(val)),
2688 ),
2689 }
2690 return hasher.final();
2691 },
2692
2693 .slice => |slice| Hash.hash(seed, asBytes(&slice.ty) ++ asBytes(&slice.ptr) ++ asBytes(&slice.len)),
2694
2695 .ptr => |ptr| {
2696 // Int-to-ptr pointers are hashed separately than decl-referencing pointers.
2697 // This is sound due to pointer provenance rules.
2698 const addr_tag: Key.Ptr.BaseAddr.Tag = ptr.base_addr;
2699 const seed2 = seed + @backingInt(addr_tag);
2700 const big_offset: i128 = ptr.byte_offset;
2701 const common = asBytes(&ptr.ty) ++ asBytes(&big_offset);
2702 return switch (ptr.base_addr) {
2703 inline .nav,
2704 .comptime_alloc,
2705 .uav,
2706 .int,
2707 .eu_payload,
2708 .opt_payload,
2709 .comptime_field,
2710 => |x| Hash.hash(seed2, common ++ asBytes(&x)),
2711
2712 .arr_elem, .field => |x| Hash.hash(
2713 seed2,
2714 common ++ asBytes(&x.base) ++ asBytes(&x.index),
2715 ),
2716 };
2717 },
2718
2719 .aggregate => |aggregate| {
2720 var hasher = Hash.init(seed);
2721 std.hash.autoHash(&hasher, aggregate.ty);
2722 const len = ip.aggregateTypeLen(aggregate.ty);
2723 const child = switch (ip.indexToKey(aggregate.ty)) {
2724 .array_type => |array_type| array_type.child,
2725 .vector_type => |vector_type| vector_type.child,
2726 .tuple_type, .struct_type => .none,
2727 else => unreachable,
2728 };
2729
2730 if (child == .u8_type) {
2731 switch (aggregate.storage) {
2732 .bytes => |bytes| for (bytes.toSlice(len, ip)) |byte| {
2733 std.hash.autoHash(&hasher, KeyTag.int);
2734 std.hash.autoHash(&hasher, byte);
2735 },
2736 .elems => |elems| for (elems[0..@intCast(len)]) |elem| {
2737 const elem_key = ip.indexToKey(elem);
2738 std.hash.autoHash(&hasher, @as(KeyTag, elem_key));
2739 switch (elem_key) {
2740 .undef => {},
2741 .int => |int| std.hash.autoHash(
2742 &hasher,
2743 @as(u8, @intCast(int.storage.u64)),
2744 ),
2745 else => unreachable,
2746 }
2747 },
2748 .repeated_elem => |elem| {
2749 const elem_key = ip.indexToKey(elem);
2750 var remaining = len;
2751 while (remaining > 0) : (remaining -= 1) {
2752 std.hash.autoHash(&hasher, @as(KeyTag, elem_key));
2753 switch (elem_key) {
2754 .undef => {},
2755 .int => |int| std.hash.autoHash(
2756 &hasher,
2757 @as(u8, @intCast(int.storage.u64)),
2758 ),
2759 else => unreachable,
2760 }
2761 }
2762 },
2763 }
2764 return hasher.final();
2765 }
2766
2767 switch (aggregate.storage) {
2768 .bytes => unreachable,
2769 .elems => |elems| for (elems[0..@intCast(len)]) |elem|
2770 std.hash.autoHash(&hasher, elem),
2771 .repeated_elem => |elem| {
2772 var remaining = len;
2773 while (remaining > 0) : (remaining -= 1) std.hash.autoHash(&hasher, elem);
2774 },
2775 }
2776 return hasher.final();
2777 },
2778
2779 .error_set_type => |x| Hash.hash(seed, std.mem.sliceAsBytes(x.names.get(ip))),
2780
2781 .tuple_type => |tuple_type| {
2782 var hasher = Hash.init(seed);
2783 for (tuple_type.types.get(ip)) |elem| std.hash.autoHash(&hasher, elem);
2784 for (tuple_type.values.get(ip)) |elem| std.hash.autoHash(&hasher, elem);
2785 return hasher.final();
2786 },
2787
2788 .func_type => |func_type| {
2789 var hasher = Hash.init(seed);
2790 func_type.hash(&hasher, ip);
2791 return hasher.final();
2792 },
2793
2794 .memoized_call => |memoized_call| {
2795 var hasher = Hash.init(seed);
2796 std.hash.autoHash(&hasher, memoized_call.func);
2797 for (memoized_call.arg_values) |arg| std.hash.autoHash(&hasher, arg);
2798 return hasher.final();
2799 },
2800
2801 .func => |func| {
2802 // In the case of a function with an inferred error set, we
2803 // must not include the inferred error set type in the hash,
2804 // otherwise we would get false negatives for interning generic
2805 // function instances which have inferred error sets.
2806
2807 if (func.generic_owner == .none and func.resolved_error_set_extra_index == 0) {
2808 const bytes = asBytes(&func.owner_nav) ++ asBytes(&func.ty) ++
2809 [1]u8{@intFromBool(func.uncoerced_ty == func.ty)};
2810 return Hash.hash(seed, bytes);
2811 }
2812
2813 var hasher = Hash.init(seed);
2814 std.hash.autoHash(&hasher, func.generic_owner);
2815 std.hash.autoHash(&hasher, func.uncoerced_ty == func.ty);
2816 for (func.comptime_args.get(ip)) |arg| std.hash.autoHash(&hasher, arg);
2817 if (func.resolved_error_set_extra_index == 0) {
2818 std.hash.autoHash(&hasher, func.ty);
2819 } else {
2820 var ty_info = ip.indexToFuncType(func.ty).?;
2821 ty_info.return_type = ip.errorUnionPayload(ty_info.return_type);
2822 ty_info.hash(&hasher, ip);
2823 }
2824 return hasher.final();
2825 },
2826
2827 .@"extern" => |e| Hash.hash(seed, asBytes(&e.name) ++
2828 asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
2829 asBytes(&e.linkage) ++ asBytes(&e.visibility) ++
2830 asBytes(&e.is_threadlocal) ++ asBytes(&e.is_dll_import) ++
2831 asBytes(&e.relocation) ++
2832 asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++
2833 asBytes(&e.zir_index) ++ &[1]u8{@backingInt(e.source)}),
2834
2835 .bitpack => |bitpack| Hash.hash(seed, asBytes(&bitpack.ty) ++ asBytes(&bitpack.backing_int_val)),
2836 };
2837 }
2838
2839 pub fn eql(a: Key, b: Key, ip: *const InternPool) bool {
2840 const KeyTag = @typeInfo(Key).@"union".tag_type.?;
2841 const a_tag: KeyTag = a;
2842 const b_tag: KeyTag = b;
2843 if (a_tag != b_tag) return false;
2844 switch (a) {
2845 .int_type => |a_info| {
2846 const b_info = b.int_type;
2847 return std.meta.eql(a_info, b_info);
2848 },
2849 .ptr_type => |a_info| {
2850 const b_info = b.ptr_type;
2851 return std.meta.eql(a_info, b_info);
2852 },
2853 .array_type => |a_info| {
2854 const b_info = b.array_type;
2855 return std.meta.eql(a_info, b_info);
2856 },
2857 .vector_type => |a_info| {
2858 const b_info = b.vector_type;
2859 return std.meta.eql(a_info, b_info);
2860 },
2861 .opt_type => |a_info| {
2862 const b_info = b.opt_type;
2863 return a_info == b_info;
2864 },
2865 .anyframe_type => |a_info| {
2866 const b_info = b.anyframe_type;
2867 return a_info == b_info;
2868 },
2869 .error_union_type => |a_info| {
2870 const b_info = b.error_union_type;
2871 return std.meta.eql(a_info, b_info);
2872 },
2873 .spirv_type => |a_info| {
2874 const b_info = b.spirv_type;
2875 return std.meta.eql(a_info, b_info);
2876 },
2877 .simple_type => |a_info| {
2878 const b_info = b.simple_type;
2879 return a_info == b_info;
2880 },
2881 .simple_value => |a_info| {
2882 const b_info = b.simple_value;
2883 return a_info == b_info;
2884 },
2885 .undef => |a_info| {
2886 const b_info = b.undef;
2887 return a_info == b_info;
2888 },
2889 .opt => |a_info| {
2890 const b_info = b.opt;
2891 return std.meta.eql(a_info, b_info);
2892 },
2893 .un => |a_info| {
2894 const b_info = b.un;
2895 return std.meta.eql(a_info, b_info);
2896 },
2897 .err => |a_info| {
2898 const b_info = b.err;
2899 return std.meta.eql(a_info, b_info);
2900 },
2901 .error_union => |a_info| {
2902 const b_info = b.error_union;
2903 return std.meta.eql(a_info, b_info);
2904 },
2905 .enum_literal => |a_info| {
2906 const b_info = b.enum_literal;
2907 return a_info == b_info;
2908 },
2909 .enum_tag => |a_info| {
2910 const b_info = b.enum_tag;
2911 return std.meta.eql(a_info, b_info);
2912 },
2913 .bitpack => |a_info| {
2914 const b_info = b.bitpack;
2915 return a_info.ty == b_info.ty and a_info.backing_int_val == b_info.backing_int_val;
2916 },
2917
2918 .@"extern" => |a_info| {
2919 const b_info = b.@"extern";
2920 return a_info.name == b_info.name and
2921 a_info.ty == b_info.ty and
2922 a_info.lib_name == b_info.lib_name and
2923 a_info.linkage == b_info.linkage and
2924 a_info.visibility == b_info.visibility and
2925 a_info.is_threadlocal == b_info.is_threadlocal and
2926 a_info.is_dll_import == b_info.is_dll_import and
2927 a_info.relocation == b_info.relocation and
2928 a_info.is_const == b_info.is_const and
2929 a_info.alignment == b_info.alignment and
2930 a_info.@"addrspace" == b_info.@"addrspace" and
2931 a_info.zir_index == b_info.zir_index and
2932 a_info.source == b_info.source;
2933 },
2934 .func => |a_info| {
2935 const b_info = b.func;
2936
2937 if (a_info.generic_owner != b_info.generic_owner)
2938 return false;
2939
2940 if (a_info.generic_owner == .none) {
2941 if (a_info.owner_nav != b_info.owner_nav)
2942 return false;
2943 } else {
2944 if (!std.mem.eql(
2945 Index,
2946 a_info.comptime_args.get(ip),
2947 b_info.comptime_args.get(ip),
2948 )) return false;
2949 }
2950
2951 if ((a_info.ty == a_info.uncoerced_ty) !=
2952 (b_info.ty == b_info.uncoerced_ty))
2953 {
2954 return false;
2955 }
2956
2957 if (a_info.ty == b_info.ty)
2958 return true;
2959
2960 // There is one case where the types may be inequal but we
2961 // still want to find the same function body instance. In the
2962 // case of the functions having an inferred error set, the key
2963 // used to find an existing function body will necessarily have
2964 // a unique inferred error set type, because it refers to the
2965 // function body InternPool Index. To make this case work we
2966 // omit the inferred error set from the equality check.
2967 if (a_info.resolved_error_set_extra_index == 0 or
2968 b_info.resolved_error_set_extra_index == 0)
2969 {
2970 return false;
2971 }
2972 var a_ty_info = ip.indexToFuncType(a_info.ty).?;
2973 a_ty_info.return_type = ip.errorUnionPayload(a_ty_info.return_type);
2974 var b_ty_info = ip.indexToFuncType(b_info.ty).?;
2975 b_ty_info.return_type = ip.errorUnionPayload(b_ty_info.return_type);
2976 return a_ty_info.eql(b_ty_info, ip);
2977 },
2978
2979 .slice => |a_info| {
2980 const b_info = b.slice;
2981 if (a_info.ty != b_info.ty) return false;
2982 if (a_info.ptr != b_info.ptr) return false;
2983 if (a_info.len != b_info.len) return false;
2984 return true;
2985 },
2986
2987 .ptr => |a_info| {
2988 const b_info = b.ptr;
2989 if (a_info.ty != b_info.ty) return false;
2990 if (a_info.byte_offset != b_info.byte_offset) return false;
2991 if (!a_info.base_addr.eql(b_info.base_addr)) return false;
2992 return true;
2993 },
2994
2995 .int => |a_info| {
2996 const b_info = b.int;
2997
2998 if (a_info.ty != b_info.ty)
2999 return false;
3000
3001 return switch (a_info.storage) {
3002 .u64 => |aa| switch (b_info.storage) {
3003 .u64 => |bb| aa == bb,
3004 .i64 => |bb| aa == bb,
3005 .big_int => |bb| bb.orderAgainstScalar(aa) == .eq,
3006 },
3007 .i64 => |aa| switch (b_info.storage) {
3008 .u64 => |bb| aa == bb,
3009 .i64 => |bb| aa == bb,
3010 .big_int => |bb| bb.orderAgainstScalar(aa) == .eq,
3011 },
3012 .big_int => |aa| switch (b_info.storage) {
3013 .u64 => |bb| aa.orderAgainstScalar(bb) == .eq,
3014 .i64 => |bb| aa.orderAgainstScalar(bb) == .eq,
3015 .big_int => |bb| aa.eql(bb),
3016 },
3017 };
3018 },
3019
3020 .float => |a_info| {
3021 const b_info = b.float;
3022
3023 if (a_info.ty != b_info.ty)
3024 return false;
3025
3026 if (a_info.ty == .c_longdouble_type and a_info.storage != .f80) {
3027 // These are strange: we'll sometimes represent them as f128, even if the
3028 // underlying type is smaller. f80 is an exception: see float_c_longdouble_f80.
3029 const a_val: u128 = switch (a_info.storage) {
3030 inline else => |val| @bitCast(@as(f128, @floatCast(val))),
3031 };
3032 const b_val: u128 = switch (b_info.storage) {
3033 inline else => |val| @bitCast(@as(f128, @floatCast(val))),
3034 };
3035 return a_val == b_val;
3036 }
3037
3038 const StorageTag = @typeInfo(Key.Float.Storage).@"union".tag_type.?;
3039 assert(@as(StorageTag, a_info.storage) == @as(StorageTag, b_info.storage));
3040
3041 switch (a_info.storage) {
3042 inline else => |val, tag| {
3043 const Bits = @Int(.unsigned, @bitSizeOf(@TypeOf(val)));
3044 const a_bits: Bits = @bitCast(val);
3045 const b_bits: Bits = @bitCast(@field(b_info.storage, @tagName(tag)));
3046 return a_bits == b_bits;
3047 },
3048 }
3049 },
3050
3051 inline .opaque_type, .enum_type, .union_type, .struct_type => |a_info, a_tag_ct| {
3052 const b_info = @field(b, @tagName(a_tag_ct));
3053 if (std.meta.activeTag(a_info) != b_info) return false;
3054 switch (a_info) {
3055 .declared => |a_d| {
3056 const b_d = b_info.declared;
3057 if (a_d.zir_index != b_d.zir_index) return false;
3058 const a_captures = switch (a_d.captures) {
3059 .owned => |s| s.get(ip),
3060 .external => |cvs| cvs,
3061 };
3062 const b_captures = switch (b_d.captures) {
3063 .owned => |s| s.get(ip),
3064 .external => |cvs| cvs,
3065 };
3066 return std.mem.eql(u32, @ptrCast(a_captures), @ptrCast(b_captures));
3067 },
3068 .reified => |a_r| {
3069 const b_r = b_info.reified;
3070 return a_r.zir_index == b_r.zir_index and
3071 a_r.type_hash == b_r.type_hash;
3072 },
3073 .generated_union_tag => |a_union_ty| return a_union_ty == b_info.generated_union_tag,
3074 }
3075 },
3076 .aggregate => |a_info| {
3077 const b_info = b.aggregate;
3078 if (a_info.ty != b_info.ty) return false;
3079
3080 const len = ip.aggregateTypeLen(a_info.ty);
3081 const StorageTag = @typeInfo(Key.Aggregate.Storage).@"union".tag_type.?;
3082 if (@as(StorageTag, a_info.storage) != @as(StorageTag, b_info.storage)) {
3083 for (0..@intCast(len)) |elem_index| {
3084 const a_elem = switch (a_info.storage) {
3085 .bytes => |bytes| ip.getIfExists(.{ .int = .{
3086 .ty = .u8_type,
3087 .storage = .{ .u64 = bytes.at(elem_index, ip) },
3088 } }) orelse return false,
3089 .elems => |elems| elems[elem_index],
3090 .repeated_elem => |elem| elem,
3091 };
3092 const b_elem = switch (b_info.storage) {
3093 .bytes => |bytes| ip.getIfExists(.{ .int = .{
3094 .ty = .u8_type,
3095 .storage = .{ .u64 = bytes.at(elem_index, ip) },
3096 } }) orelse return false,
3097 .elems => |elems| elems[elem_index],
3098 .repeated_elem => |elem| elem,
3099 };
3100 if (a_elem != b_elem) return false;
3101 }
3102 return true;
3103 }
3104
3105 switch (a_info.storage) {
3106 .bytes => |a_bytes| {
3107 const b_bytes = b_info.storage.bytes;
3108 return a_bytes == b_bytes or
3109 std.mem.eql(u8, a_bytes.toSlice(len, ip), b_bytes.toSlice(len, ip));
3110 },
3111 .elems => |a_elems| {
3112 const b_elems = b_info.storage.elems;
3113 return std.mem.eql(
3114 Index,
3115 a_elems[0..@intCast(len)],
3116 b_elems[0..@intCast(len)],
3117 );
3118 },
3119 .repeated_elem => |a_elem| {
3120 const b_elem = b_info.storage.repeated_elem;
3121 return a_elem == b_elem;
3122 },
3123 }
3124 },
3125 .tuple_type => |a_info| {
3126 const b_info = b.tuple_type;
3127 return std.mem.eql(Index, a_info.types.get(ip), b_info.types.get(ip)) and
3128 std.mem.eql(Index, a_info.values.get(ip), b_info.values.get(ip));
3129 },
3130 .error_set_type => |a_info| {
3131 const b_info = b.error_set_type;
3132 return std.mem.eql(NullTerminatedString, a_info.names.get(ip), b_info.names.get(ip));
3133 },
3134 .inferred_error_set_type => |a_info| {
3135 const b_info = b.inferred_error_set_type;
3136 return a_info == b_info;
3137 },
3138
3139 .func_type => |a_info| {
3140 const b_info = b.func_type;
3141 return Key.FuncType.eql(a_info, b_info, ip);
3142 },
3143
3144 .memoized_call => |a_info| {
3145 const b_info = b.memoized_call;
3146 return a_info.func == b_info.func and
3147 std.mem.eql(Index, a_info.arg_values, b_info.arg_values);
3148 },
3149 }
3150 }
3151
3152 pub fn typeOf(key: Key) Index {
3153 return switch (key) {
3154 .int_type,
3155 .ptr_type,
3156 .array_type,
3157 .vector_type,
3158 .opt_type,
3159 .anyframe_type,
3160 .error_union_type,
3161 .error_set_type,
3162 .inferred_error_set_type,
3163 .simple_type,
3164 .struct_type,
3165 .union_type,
3166 .spirv_type,
3167 .opaque_type,
3168 .enum_type,
3169 .tuple_type,
3170 .func_type,
3171 => .type_type,
3172
3173 inline .ptr,
3174 .slice,
3175 .int,
3176 .float,
3177 .opt,
3178 .@"extern",
3179 .func,
3180 .err,
3181 .error_union,
3182 .enum_tag,
3183 .aggregate,
3184 .un,
3185 .bitpack,
3186 => |x| x.ty,
3187
3188 .enum_literal => .enum_literal_type,
3189
3190 .undef => |x| x,
3191
3192 .simple_value => |s| switch (s) {
3193 .void => .void_type,
3194 .null => .null_type,
3195 .false, .true => .bool_type,
3196 .@"unreachable" => .noreturn_type,
3197 },
3198
3199 .memoized_call => unreachable,
3200 };
3201 }
3202};
3203
3204pub const LoadedStructType = struct {
3205 /// Index of the `struct_decl` or `reify` ZIR instruction.
3206 zir_index: TrackedInst.Index,
3207 captures: CaptureValue.Slice,
3208 is_reified: bool,
3209
3210 /// The name of this struct type.
3211 name: NullTerminatedString,
3212 /// The fully-qualified name of this struct type.
3213 fqn: NullTerminatedString,
3214 /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
3215 /// Otherwise, or if this is a file's root struct type, this is `.none`.
3216 name_nav: Nav.Index.Optional,
3217 namespace: NamespaceIndex,
3218
3219 layout: std.lang.Type.ContainerLayout,
3220 /// May be `undefined` if `layout != .@"packed"`.
3221 packed_backing_mode: BackingTypeMode,
3222
3223 /// Initially `false`, and set to `true` once any dependency on or reference to the struct's
3224 /// layout is encountered, after which it is never reset to `false`, even across incremental
3225 /// updates.
3226 ///
3227 /// This field is purely an optimization to avoid resolving the layout of types whose layouts
3228 /// are never demanded. If this field is `true` but the layout is not actually needed, the
3229 /// compiler frontend resolves this by traversing the reference graph at the end of each update
3230 /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis.
3231 want_layout: bool,
3232
3233 // The remaining fields are only valid once the struct's layout is resolved.
3234 field_name_map: MapIndex,
3235 field_names: NullTerminatedString.Slice,
3236 field_types: Index.Slice,
3237 field_defaults: Index.Slice,
3238 field_aligns: Alignment.Slice,
3239 field_is_comptime_bits: ComptimeBits,
3240 /// If `layout` is `.@"packed"`, this is `.empty`.
3241 field_runtime_order: RuntimeOrder.Slice,
3242 /// If `layout` is `.@"packed"`, this is `.empty`.
3243 field_offsets: Offsets,
3244 /// Only valid if `layout` is `.@"packed"`.
3245 packed_backing_int_type: Index,
3246 /// Only valid if `layout` is *not* `.@"packed"`.
3247 class: TypeClass,
3248 /// Only valid if `layout` is *not* `.@"packed"`.
3249 size: u32,
3250 /// Only valid if `layout` is *not* `.@"packed"`.
3251 alignment: Alignment,
3252
3253 pub const ComptimeBits = struct {
3254 tid: Zcu.PerThread.Id,
3255 start: u32,
3256 /// This is the number of u32 elements, not the number of struct fields.
3257 len: u32,
3258
3259 pub const empty: ComptimeBits = .{ .tid = .main, .start = 0, .len = 0 };
3260
3261 pub fn getAll(this: ComptimeBits, ip: *const InternPool) []u32 {
3262 const extra = ip.getLocalShared(this.tid).extra.acquire();
3263 return extra.view().items(.@"0")[this.start..][0..this.len];
3264 }
3265
3266 pub fn get(this: ComptimeBits, ip: *const InternPool, i: usize) bool {
3267 if (this.len == 0) return false;
3268 return @as(u1, @truncate(this.getAll(ip)[i / 32] >> @intCast(i % 32))) != 0;
3269 }
3270 };
3271
3272 pub const Offsets = struct {
3273 tid: Zcu.PerThread.Id,
3274 start: u32,
3275 len: u32,
3276
3277 pub const empty: Offsets = .{ .tid = .main, .start = 0, .len = 0 };
3278
3279 pub fn get(this: Offsets, ip: *const InternPool) []u32 {
3280 const extra = ip.getLocalShared(this.tid).extra.acquire();
3281 return @ptrCast(extra.view().items(.@"0")[this.start..][0..this.len]);
3282 }
3283 };
3284
3285 pub const RuntimeOrder = enum(u32) {
3286 /// Placeholder until layout is resolved.
3287 unresolved = std.math.maxInt(u32) - 0,
3288 /// Field not present at runtime
3289 omitted = std.math.maxInt(u32) - 1,
3290 _,
3291
3292 pub const Slice = struct {
3293 tid: Zcu.PerThread.Id,
3294 start: u32,
3295 len: u32,
3296
3297 pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 };
3298
3299 pub fn get(slice: RuntimeOrder.Slice, ip: *const InternPool) []RuntimeOrder {
3300 const extra = ip.getLocalShared(slice.tid).extra.acquire();
3301 return @ptrCast(extra.view().items(.@"0")[slice.start..][0..slice.len]);
3302 }
3303 };
3304
3305 pub fn toInt(i: RuntimeOrder) ?u32 {
3306 return switch (i) {
3307 .omitted => null,
3308 .unresolved => unreachable,
3309 else => @backingInt(i),
3310 };
3311 }
3312 };
3313
3314 /// Look up field index based on field name.
3315 pub fn nameIndex(s: LoadedStructType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
3316 const map = s.field_name_map.get(ip);
3317 const adapter: NullTerminatedString.Adapter = .{ .strings = s.field_names.get(ip) };
3318 const field_index = map.getIndexAdapted(name, adapter) orelse return null;
3319 return @intCast(field_index);
3320 }
3321
3322 /// Iterates over non-comptime fields in the order they are laid out in memory at runtime.
3323 /// May or may not include zero-bit fields.
3324 /// Asserts the struct is not packed.
3325 pub fn iterateRuntimeOrder(s: *const LoadedStructType, ip: *const InternPool) RuntimeOrderIterator {
3326 switch (s.layout) {
3327 .auto => {
3328 const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted);
3329 return .{
3330 .runtime_order = ro,
3331 .fields_len = @intCast(ro.len),
3332 .next_index = 0,
3333 };
3334 },
3335 .@"extern" => return .{
3336 .runtime_order = null,
3337 .fields_len = s.field_names.len,
3338 .next_index = 0,
3339 },
3340 .@"packed" => unreachable,
3341 }
3342 }
3343 pub const RuntimeOrderIterator = struct {
3344 runtime_order: ?[]const RuntimeOrder,
3345 fields_len: u32,
3346 next_index: u32,
3347 pub fn next(it: *RuntimeOrderIterator) ?u32 {
3348 const i = it.next_index;
3349 if (i == it.fields_len) return null;
3350 it.next_index = i + 1;
3351 const ro = it.runtime_order orelse return i;
3352 return ro[i].toInt().?;
3353 }
3354 };
3355
3356 pub fn iterateRuntimeOrderReverse(s: *const LoadedStructType, ip: *InternPool) ReverseRuntimeOrderIterator {
3357 switch (s.layout) {
3358 .auto => {
3359 const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted);
3360 return .{
3361 .runtime_order = ro,
3362 .last_index = @intCast(ro.len),
3363 };
3364 },
3365 .@"extern" => return .{
3366 .runtime_order = null,
3367 .last_index = s.field_names.len,
3368 },
3369 .@"packed" => unreachable,
3370 }
3371 }
3372 pub const ReverseRuntimeOrderIterator = struct {
3373 runtime_order: ?[]const RuntimeOrder,
3374 last_index: u32,
3375 pub fn next(it: *ReverseRuntimeOrderIterator) ?u32 {
3376 if (it.last_index == 0) return null;
3377 const i = it.last_index - 1;
3378 it.last_index = i;
3379 const ro = it.runtime_order orelse return i;
3380 return ro[i].toInt().?;
3381 }
3382 };
3383};
3384
3385/// Unlike `Tag.TypeUnion` which is an encoding, and `Key.UnionType` which is a
3386/// minimal hashmap key, this type is a convenience type that contains info
3387/// needed by semantic analysis.
3388pub const LoadedUnionType = struct {
3389 /// Index of the `union_decl` or `reify` ZIR instruction.
3390 zir_index: TrackedInst.Index,
3391 captures: CaptureValue.Slice,
3392 is_reified: bool,
3393
3394 /// The name of this union type.
3395 name: NullTerminatedString,
3396 /// The fully-qualified name of this union type.
3397 fqn: NullTerminatedString,
3398 /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
3399 /// Otherwise, this is `.none`.
3400 name_nav: Nav.Index.Optional,
3401 namespace: NamespaceIndex,
3402
3403 layout: std.lang.Type.ContainerLayout,
3404 enum_tag_mode: BackingTypeMode,
3405 /// May be `undefined` if `layout != .@"packed"`.
3406 packed_backing_mode: BackingTypeMode,
3407
3408 /// Only reified unions store field names; typically they should be loaded from `enum_tag_type`
3409 /// instead. Reified unions store them because type resolution needs them in order to validate
3410 /// or populate `enum_tag_type`.
3411 reified_field_names: NullTerminatedString.Slice,
3412
3413 /// Initially `false`, and set to `true` once any dependency on or reference to the struct's
3414 /// layout is encountered, after which it is never reset to `false`, even across incremental
3415 /// updates.
3416 ///
3417 /// This field is purely an optimization to avoid resolving the layout of types whose layouts
3418 /// are never demanded. If this field is `true` but the layout is not actually needed, the
3419 /// compiler frontend resolves this by traversing the reference graph at the end of each update
3420 /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis.
3421 want_layout: bool,
3422
3423 // The remaining fields are only valid once the union's layout is resolved.
3424 field_types: Index.Slice,
3425 field_aligns: Alignment.Slice,
3426 tag_usage: TagUsage,
3427 /// While `tag_usage` indicates whether the union should logically contain a tag, it may be
3428 /// omitted if the union layout is resolved as OPV or NPV. This field is `true` iff there is an
3429 /// actual runtime tag, with one or more runtime bits, in the union layout. It is always `false`
3430 /// if `layout` is not `.auto`.
3431 has_runtime_tag: bool,
3432 /// Even if `tag_usage == .none` and `has_runtime_tag == false`, this is still populated with
3433 /// the union's "hypothetical" tag type.
3434 enum_tag_type: Index,
3435 /// Only valid if `layout` is `.@"packed"`.
3436 packed_backing_int_type: Index,
3437 /// Not valid if `layout` is `.@"packed"`.
3438 class: TypeClass,
3439 /// Not valid if `layout` is `.@"packed"`.
3440 size: u32,
3441 /// Not valid if `layout` is `.@"packed"`.
3442 padding: u32,
3443 /// Not valid if `layout` is `.@"packed"`.
3444 alignment: Alignment,
3445
3446 pub const TagUsage = enum(u2) {
3447 none,
3448 safety,
3449 tagged,
3450 };
3451};
3452
3453pub const LoadedEnumType = struct {
3454 /// This is `none` iff this is a generated tag type.
3455 /// Otherwise, index of the `enum_decl` or `reify` ZIR instruction.
3456 zir_index: TrackedInst.Index.Optional,
3457 captures: CaptureValue.Slice,
3458 /// If `zir_index` is `.none`, this is the union type for which this enum is the tag type.
3459 owner_union: Index,
3460 is_reified: bool,
3461
3462 /// The name of this enum type.
3463 name: NullTerminatedString,
3464 /// The fully-qualified name of this enum type.
3465 fqn: NullTerminatedString,
3466 /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
3467 /// Otherwise, this is `.none`.
3468 name_nav: Nav.Index.Optional,
3469 namespace: NamespaceIndex,
3470
3471 int_tag_mode: BackingTypeMode,
3472 nonexhaustive: bool,
3473
3474 /// Initially `false`, and set to `true` once any dependency on or reference to the struct's
3475 /// layout is encountered, after which it is never reset to `false`, even across incremental
3476 /// updates.
3477 ///
3478 /// This field is purely an optimization to avoid resolving the layout of types whose layouts
3479 /// are never demanded. If this field is `true` but the layout is not actually needed, the
3480 /// compiler frontend resolves this by traversing the reference graph at the end of each update
3481 /// with `Zcu.resolveReferences` and hiding compile errors which arise from this analysis.
3482 want_layout: bool,
3483
3484 // The remaining fields are only valid once the enum's layout is resolved.
3485 int_tag_type: Index,
3486 field_name_map: MapIndex,
3487 field_names: NullTerminatedString.Slice,
3488 field_value_map: OptionalMapIndex,
3489 field_values: Index.Slice,
3490
3491 /// Look up field index based on field name.
3492 pub fn nameIndex(e: LoadedEnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
3493 const map = e.field_name_map.get(ip);
3494 const adapter: NullTerminatedString.Adapter = .{ .strings = e.field_names.get(ip) };
3495 const field_index = map.getIndexAdapted(name, adapter) orelse return null;
3496 return @intCast(field_index);
3497 }
3498
3499 /// Look up field index based on integer tag value.
3500 /// Asserts that the type of `tag_val` is `enum_obj.int_tag_type`.
3501 /// Asserts that `tag_val` is not `undefined`.
3502 pub fn tagValueIndex(e: LoadedEnumType, ip: *const InternPool, tag_val: Index) ?u32 {
3503 assert(ip.typeOf(tag_val) == e.int_tag_type);
3504 assert(ip.indexToKey(tag_val) == .int);
3505 if (e.field_value_map.unwrap()) |field_value_map| {
3506 const map = field_value_map.get(ip);
3507 const adapter: Index.Adapter = .{ .indexes = e.field_values.get(ip) };
3508 const field_index = map.getIndexAdapted(tag_val, adapter) orelse return null;
3509 return @intCast(field_index);
3510 }
3511 // Auto-numbered enum, so convert `tag_val` to field index
3512 const field_index = switch (ip.indexToKey(tag_val).int.storage) {
3513 inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null,
3514 .big_int => |x| x.toInt(u32) catch return null,
3515 };
3516 return if (field_index < e.field_names.len) field_index else null;
3517 }
3518};
3519
3520pub const LoadedOpaqueType = struct {
3521 /// Index of the `opaque_decl` instruction.
3522 zir_index: TrackedInst.Index,
3523 captures: CaptureValue.Slice,
3524
3525 /// The name of this opaque type.
3526 name: NullTerminatedString,
3527 /// The fully-qualified name of this opaque type.
3528 fqn: NullTerminatedString,
3529 /// If this is a declared type with the `.parent` name strategy, this is the `Nav` it was named after.
3530 /// Otherwise, this is `.none`.
3531 name_nav: Nav.Index.Optional,
3532 namespace: NamespaceIndex,
3533};
3534
3535pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
3536 const unwrapped_index = index.unwrap(ip);
3537 const extra_list = unwrapped_index.getExtra(ip);
3538 const extra_items = extra_list.view().items(.@"0");
3539 const item = unwrapped_index.getItem(ip);
3540 // Exiting this `switch` means this is a `packed struct`.
3541 const backing_mode: BackingTypeMode, const any_defaults: bool = switch (item.tag) {
3542 .type_struct_packed_auto => .{ .auto, false },
3543 .type_struct_packed_explicit => .{ .explicit, false },
3544 .type_struct_packed_auto_defaults => .{ .auto, true },
3545 .type_struct_packed_explicit_defaults => .{ .explicit, true },
3546 .type_struct => {
3547 const extra = extraDataTrail(extra_list, Tag.TypeStruct, item.data);
3548 var extra_index = extra.end;
3549 const captures: CaptureValue.Slice = switch (extra.data.flags.any_captures) {
3550 .reified => captures: {
3551 extra_index += 2; // type_hash: PackedU64
3552 break :captures .empty;
3553 },
3554 .false => .empty,
3555 .true => captures: {
3556 const len = extra_items[extra_index];
3557 extra_index += 1;
3558 break :captures .{
3559 .tid = unwrapped_index.tid,
3560 .start = extra_index,
3561 .len = len,
3562 };
3563 },
3564 };
3565 extra_index += captures.len;
3566 const field_names: NullTerminatedString.Slice = .{
3567 .tid = unwrapped_index.tid,
3568 .start = extra_index,
3569 .len = extra.data.fields_len,
3570 };
3571 extra_index += field_names.len;
3572 const field_types: Index.Slice = .{
3573 .tid = unwrapped_index.tid,
3574 .start = extra_index,
3575 .len = extra.data.fields_len,
3576 };
3577 extra_index += field_types.len;
3578 const field_defaults: Index.Slice = if (extra.data.flags.any_field_defaults) .{
3579 .tid = unwrapped_index.tid,
3580 .start = extra_index,
3581 .len = extra.data.fields_len,
3582 } else .empty;
3583 extra_index += field_defaults.len;
3584 const field_aligns: Alignment.Slice = if (extra.data.flags.any_field_aligns) .{
3585 .tid = unwrapped_index.tid,
3586 .start = extra_index,
3587 .len = extra.data.fields_len,
3588 } else .empty;
3589 extra_index += @divCeil(field_aligns.len, 4);
3590 const field_is_comptime_bits: LoadedStructType.ComptimeBits = if (extra.data.flags.any_comptime_fields) .{
3591 .tid = unwrapped_index.tid,
3592 .start = extra_index,
3593 .len = @divCeil(extra.data.fields_len, 32),
3594 } else .empty;
3595 extra_index += field_is_comptime_bits.len;
3596 const field_runtime_order: LoadedStructType.RuntimeOrder.Slice = if (extra.data.flags.layout == .auto) .{
3597 .tid = unwrapped_index.tid,
3598 .start = extra_index,
3599 .len = extra.data.fields_len,
3600 } else .empty;
3601 extra_index += field_runtime_order.len;
3602 const field_offsets: LoadedStructType.Offsets = .{
3603 .tid = unwrapped_index.tid,
3604 .start = extra_index,
3605 .len = extra.data.fields_len,
3606 };
3607 extra_index += field_offsets.len;
3608
3609 return .{
3610 .zir_index = extra.data.zir_index,
3611 .captures = captures,
3612 .is_reified = extra.data.flags.any_captures == .reified,
3613 .name = extra.data.name,
3614 .fqn = extra.data.fqn,
3615 .name_nav = extra.data.name_nav,
3616 .namespace = extra.data.namespace,
3617 .layout = switch (extra.data.flags.layout) {
3618 .auto => .auto,
3619 .@"extern" => .@"extern",
3620 },
3621 .packed_backing_mode = undefined,
3622
3623 .want_layout = extra.data.flags.want_layout,
3624
3625 .field_name_map = extra.data.field_name_map,
3626 .field_names = field_names,
3627 .field_types = field_types,
3628 .field_defaults = field_defaults,
3629 .field_aligns = field_aligns,
3630 .field_is_comptime_bits = field_is_comptime_bits,
3631 .field_runtime_order = field_runtime_order,
3632 .field_offsets = field_offsets,
3633 .packed_backing_int_type = .none,
3634 .class = extra.data.flags.class,
3635 .size = extra.data.size,
3636 .alignment = extra.data.flags.alignment,
3637 };
3638 },
3639 else => unreachable,
3640 };
3641 const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, item.data);
3642 var extra_index = extra.end;
3643 const captures: CaptureValue.Slice = switch (extra.data.bits.captures_len) {
3644 .reified => captures: {
3645 extra_index += 2; // type_hash: PackedU64
3646 break :captures .empty;
3647 },
3648 _ => |n| .{
3649 .tid = unwrapped_index.tid,
3650 .start = extra_index,
3651 .len = @backingInt(n),
3652 },
3653 };
3654 extra_index += captures.len;
3655 const field_names: NullTerminatedString.Slice = .{
3656 .tid = unwrapped_index.tid,
3657 .start = extra_index,
3658 .len = extra.data.fields_len,
3659 };
3660 extra_index += field_names.len;
3661 const field_types: Index.Slice = .{
3662 .tid = unwrapped_index.tid,
3663 .start = extra_index,
3664 .len = extra.data.fields_len,
3665 };
3666 extra_index += field_types.len;
3667 const field_defaults: Index.Slice = if (any_defaults) .{
3668 .tid = unwrapped_index.tid,
3669 .start = extra_index,
3670 .len = extra.data.fields_len,
3671 } else .empty;
3672 extra_index += field_defaults.len;
3673 return .{
3674 .zir_index = extra.data.zir_index,
3675 .captures = captures,
3676 .is_reified = extra.data.bits.captures_len == .reified,
3677 .name = extra.data.name,
3678 .fqn = extra.data.fqn,
3679 .name_nav = extra.data.name_nav,
3680 .namespace = extra.data.namespace,
3681 .layout = .@"packed",
3682 .packed_backing_mode = backing_mode,
3683
3684 .want_layout = extra.data.bits.want_layout,
3685
3686 .field_name_map = extra.data.field_name_map,
3687 .field_names = field_names,
3688 .field_types = field_types,
3689 .field_defaults = field_defaults,
3690 .field_aligns = .empty,
3691 .field_is_comptime_bits = .empty,
3692 .field_runtime_order = .empty,
3693 .field_offsets = .empty,
3694 .packed_backing_int_type = extra.data.backing_int_type,
3695 .class = undefined,
3696 .size = undefined,
3697 .alignment = undefined,
3698 };
3699}
3700
3701pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
3702 const unwrapped_index = index.unwrap(ip);
3703 const extra_list = unwrapped_index.getExtra(ip);
3704 const extra_items = extra_list.view().items(.@"0");
3705 const item = unwrapped_index.getItem(ip);
3706 // Exiting this `switch` means this is a `packed union`.
3707 const backing_mode: BackingTypeMode = switch (item.tag) {
3708 .type_union_packed_auto => .auto,
3709 .type_union_packed_explicit => .explicit,
3710 .type_union => {
3711 const extra = extraDataTrail(extra_list, Tag.TypeUnion, item.data);
3712 var extra_index = extra.end;
3713 const captures: CaptureValue.Slice = switch (extra.data.flags.any_captures) {
3714 .reified => captures: {
3715 extra_index += 2; // type_hash: PackedU64
3716 break :captures .empty;
3717 },
3718 .false => .empty,
3719 .true => captures: {
3720 const len = extra_items[extra_index];
3721 extra_index += 1;
3722 break :captures .{
3723 .tid = unwrapped_index.tid,
3724 .start = extra_index,
3725 .len = len,
3726 };
3727 },
3728 };
3729 extra_index += captures.len;
3730 const reified_field_names: NullTerminatedString.Slice = if (extra.data.flags.any_captures == .reified) .{
3731 .tid = unwrapped_index.tid,
3732 .start = extra_index,
3733 .len = extra.data.fields_len,
3734 } else .empty;
3735 extra_index += reified_field_names.len;
3736 const field_types: Index.Slice = .{
3737 .tid = unwrapped_index.tid,
3738 .start = extra_index,
3739 .len = extra.data.fields_len,
3740 };
3741 extra_index += field_types.len;
3742 const field_aligns: Alignment.Slice = if (extra.data.flags.any_field_aligns) .{
3743 .tid = unwrapped_index.tid,
3744 .start = extra_index,
3745 .len = extra.data.fields_len,
3746 } else .empty;
3747 extra_index += @divCeil(field_aligns.len, 4);
3748
3749 return .{
3750 .zir_index = extra.data.zir_index,
3751 .captures = captures,
3752 .is_reified = extra.data.flags.any_captures == .reified,
3753 .name = extra.data.name,
3754 .fqn = extra.data.fqn,
3755 .name_nav = extra.data.name_nav,
3756 .namespace = extra.data.namespace,
3757 .layout = switch (extra.data.flags.layout) {
3758 .auto => .auto,
3759 .@"extern" => .@"extern",
3760 },
3761 .tag_usage = extra.data.flags.tag_usage,
3762 .enum_tag_mode = extra.data.flags.enum_tag_mode,
3763 .enum_tag_type = extra.data.enum_tag_type,
3764 .packed_backing_mode = undefined,
3765 .packed_backing_int_type = undefined,
3766 .reified_field_names = reified_field_names,
3767 .want_layout = extra.data.flags.want_layout,
3768 .field_types = field_types,
3769 .field_aligns = field_aligns,
3770 .has_runtime_tag = extra.data.flags.has_runtime_tag,
3771 .class = extra.data.flags.class,
3772 .size = extra.data.size,
3773 .padding = extra.data.padding,
3774 .alignment = extra.data.flags.alignment,
3775 };
3776 },
3777 else => unreachable,
3778 };
3779 const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, item.data);
3780 var extra_index = extra.end;
3781 const captures: CaptureValue.Slice = switch (extra.data.bits.captures_len) {
3782 .reified => captures: {
3783 extra_index += 2; // type_hash: PackedU64
3784 break :captures .empty;
3785 },
3786 _ => |n| .{
3787 .tid = unwrapped_index.tid,
3788 .start = extra_index,
3789 .len = @backingInt(n),
3790 },
3791 };
3792 extra_index += captures.len;
3793 const reified_field_names: NullTerminatedString.Slice = if (extra.data.bits.captures_len == .reified) .{
3794 .tid = unwrapped_index.tid,
3795 .start = extra_index,
3796 .len = extra.data.fields_len,
3797 } else .empty;
3798 extra_index += reified_field_names.len;
3799 const field_types: Index.Slice = .{
3800 .tid = unwrapped_index.tid,
3801 .start = extra_index,
3802 .len = extra.data.fields_len,
3803 };
3804 extra_index += field_types.len;
3805 return .{
3806 .zir_index = extra.data.zir_index,
3807 .captures = captures,
3808 .is_reified = extra.data.bits.captures_len == .reified,
3809 .name = extra.data.name,
3810 .fqn = extra.data.fqn,
3811 .name_nav = extra.data.name_nav,
3812 .namespace = extra.data.namespace,
3813 .layout = .@"packed",
3814 .tag_usage = .none,
3815 .enum_tag_mode = .auto,
3816 .enum_tag_type = extra.data.enum_tag_type,
3817 .packed_backing_mode = backing_mode,
3818 .packed_backing_int_type = extra.data.backing_int_type,
3819 .reified_field_names = reified_field_names,
3820 .want_layout = extra.data.bits.want_layout,
3821 .field_types = field_types,
3822 .field_aligns = .empty,
3823 .has_runtime_tag = false,
3824 .class = undefined,
3825 .size = undefined,
3826 .padding = undefined,
3827 .alignment = undefined,
3828 };
3829}
3830
3831pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
3832 const unwrapped_index = index.unwrap(ip);
3833 const extra_list = unwrapped_index.getExtra(ip);
3834 const extra_items = extra_list.view().items(.@"0");
3835 const item = unwrapped_index.getItem(ip);
3836 const explicit_int_tag: bool, const nonexhaustive: bool = switch (item.tag) {
3837 .type_enum_auto => .{ false, false },
3838 .type_enum_explicit => .{ true, false },
3839 .type_enum_nonexhaustive => .{ true, true },
3840 else => unreachable,
3841 };
3842 const extra = extraDataTrail(extra_list, Tag.TypeEnum, item.data);
3843 var extra_index: u32 = @intCast(extra.end);
3844 const zir_index: TrackedInst.Index.Optional, const captures: CaptureValue.Slice, const owner_union: Index = switch (extra.data.bits.captures_len) {
3845 .reified => info: {
3846 const zir_index: TrackedInst.Index = @fromBackingInt(@intCast(extra_items[extra_index]));
3847 extra_index += 1;
3848 extra_index += 2; // type_hash: PackedU64
3849 break :info .{ zir_index.toOptional(), .empty, .none };
3850 },
3851 .generated_union_tag => info: {
3852 const owner_union: Index = @fromBackingInt(@intCast(extra_items[extra_index]));
3853 extra_index += 1;
3854 break :info .{ .none, .empty, owner_union };
3855 },
3856 _ => |n| info: {
3857 const zir_index: TrackedInst.Index = @fromBackingInt(@intCast(extra_items[extra_index]));
3858 extra_index += 1;
3859 const captures: CaptureValue.Slice = .{
3860 .tid = unwrapped_index.tid,
3861 .start = extra_index,
3862 .len = @backingInt(n),
3863 };
3864 extra_index += captures.len;
3865 break :info .{ zir_index.toOptional(), captures, .none };
3866 },
3867 };
3868 const field_value_map: OptionalMapIndex = if (explicit_int_tag) m: {
3869 const map: MapIndex = @fromBackingInt(@intCast(extra_items[extra_index]));
3870 extra_index += 1;
3871 break :m map.toOptional();
3872 } else .none;
3873 const field_names: NullTerminatedString.Slice = .{
3874 .tid = unwrapped_index.tid,
3875 .start = extra_index,
3876 .len = extra.data.fields_len,
3877 };
3878 extra_index += field_names.len;
3879 const field_values: Index.Slice = if (explicit_int_tag) .{
3880 .tid = unwrapped_index.tid,
3881 .start = extra_index,
3882 .len = extra.data.fields_len,
3883 } else .empty;
3884 extra_index += field_values.len;
3885 return .{
3886 .zir_index = zir_index,
3887 .captures = captures,
3888 .is_reified = extra.data.bits.captures_len == .reified,
3889 .owner_union = owner_union,
3890 .name = extra.data.name,
3891 .fqn = extra.data.fqn,
3892 .name_nav = extra.data.name_nav,
3893 .namespace = extra.data.namespace,
3894 .int_tag_type = extra.data.int_tag_type,
3895 .int_tag_mode = if (explicit_int_tag) .explicit else .auto,
3896 .nonexhaustive = nonexhaustive,
3897 .want_layout = extra.data.bits.want_layout,
3898 .field_name_map = extra.data.field_name_map,
3899 .field_value_map = field_value_map,
3900 .field_names = field_names,
3901 .field_values = field_values,
3902 };
3903}
3904
3905pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType {
3906 const unwrapped_index = index.unwrap(ip);
3907 const item = unwrapped_index.getItem(ip);
3908 assert(item.tag == .type_opaque);
3909 const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, item.data);
3910 return .{
3911 .zir_index = extra.data.zir_index,
3912 .captures = .{
3913 .tid = unwrapped_index.tid,
3914 .start = extra.end,
3915 .len = extra.data.captures_len,
3916 },
3917 .name = extra.data.name,
3918 .fqn = extra.data.fqn,
3919 .name_nav = extra.data.name_nav,
3920 .namespace = extra.data.namespace,
3921 };
3922}
3923
3924pub fn loadSpirvType(ip: *const InternPool, index: Index) Tag.TypeSpirv {
3925 const unwrapped_index = index.unwrap(ip);
3926 const item = unwrapped_index.getItem(ip);
3927 assert(item.tag == .type_spirv);
3928 const extra = extraData(unwrapped_index.getExtra(ip), Tag.TypeSpirv, item.data);
3929 return extra;
3930}
3931
3932pub const Item = struct {
3933 tag: Tag,
3934 /// The doc comments on the respective Tag explain how to interpret this.
3935 data: u32,
3936};
3937
3938/// Represents an index into `map`. It represents the canonical index
3939/// of a `Value` within this `InternPool`. The values are typed.
3940/// Two values which have the same type can be equality compared simply
3941/// by checking if their indexes are equal, provided they are both in
3942/// the same `InternPool`.
3943/// When adding a tag to this enum, consider adding a corresponding entry to
3944/// `primitives` in AstGen.zig.
3945pub const Index = enum(u32) {
3946 pub const first_type: Index = .u0_type;
3947 pub const last_type: Index = .empty_tuple_type;
3948 pub const first_value: Index = .undef;
3949 pub const last_value: Index = .empty_tuple;
3950
3951 u0_type,
3952 u1_type,
3953 u8_type,
3954 i8_type,
3955 u16_type,
3956 i16_type,
3957 u29_type,
3958 u32_type,
3959 i32_type,
3960 u64_type,
3961 i64_type,
3962 u80_type,
3963 u128_type,
3964 i128_type,
3965 u256_type,
3966 usize_type,
3967 isize_type,
3968 c_char_type,
3969 c_short_type,
3970 c_ushort_type,
3971 c_int_type,
3972 c_uint_type,
3973 c_long_type,
3974 c_ulong_type,
3975 c_longlong_type,
3976 c_ulonglong_type,
3977 c_longdouble_type,
3978 f16_type,
3979 f32_type,
3980 f64_type,
3981 f80_type,
3982 f128_type,
3983 anyopaque_type,
3984 bool_type,
3985 void_type,
3986 type_type,
3987 anyerror_type,
3988 comptime_int_type,
3989 comptime_float_type,
3990 noreturn_type,
3991 anyframe_type,
3992 null_type,
3993 undefined_type,
3994 enum_literal_type,
3995
3996 ptr_usize_type,
3997 ptr_const_comptime_int_type,
3998 manyptr_u8_type,
3999 manyptr_const_u8_type,
4000 manyptr_const_u8_sentinel_0_type,
4001 slice_const_u8_type,
4002 slice_const_u8_sentinel_0_type,
4003
4004 manyptr_const_slice_const_u8_type,
4005 slice_const_slice_const_u8_type,
4006
4007 optional_type_type,
4008 manyptr_const_type_type,
4009 slice_const_type_type,
4010
4011 vector_8_i8_type,
4012 vector_16_i8_type,
4013 vector_32_i8_type,
4014 vector_64_i8_type,
4015 vector_1_u8_type,
4016 vector_2_u8_type,
4017 vector_4_u8_type,
4018 vector_8_u8_type,
4019 vector_16_u8_type,
4020 vector_32_u8_type,
4021 vector_64_u8_type,
4022 vector_2_i16_type,
4023 vector_4_i16_type,
4024 vector_8_i16_type,
4025 vector_16_i16_type,
4026 vector_32_i16_type,
4027 vector_4_u16_type,
4028 vector_8_u16_type,
4029 vector_16_u16_type,
4030 vector_32_u16_type,
4031 vector_2_i32_type,
4032 vector_4_i32_type,
4033 vector_8_i32_type,
4034 vector_16_i32_type,
4035 vector_4_u32_type,
4036 vector_8_u32_type,
4037 vector_16_u32_type,
4038 vector_2_i64_type,
4039 vector_4_i64_type,
4040 vector_8_i64_type,
4041 vector_2_u64_type,
4042 vector_4_u64_type,
4043 vector_8_u64_type,
4044 vector_1_u128_type,
4045 vector_2_u128_type,
4046 vector_1_u256_type,
4047 vector_4_f16_type,
4048 vector_8_f16_type,
4049 vector_16_f16_type,
4050 vector_32_f16_type,
4051 vector_2_f32_type,
4052 vector_4_f32_type,
4053 vector_8_f32_type,
4054 vector_16_f32_type,
4055 vector_2_f64_type,
4056 vector_4_f64_type,
4057 vector_8_f64_type,
4058
4059 optional_noreturn_type,
4060 anyerror_void_error_union_type,
4061 /// Used for the inferred error set of inline/comptime function calls.
4062 adhoc_inferred_error_set_type,
4063 /// Represents a type which is unknown.
4064 /// This is used in functions to represent generic parameter/return types, and
4065 /// during semantic analysis to represent unknown result types (i.e. where AstGen
4066 /// thought we would have a result type, but we do not).
4067 generic_poison_type,
4068 /// `@TypeOf(.{})`; a tuple with zero elements.
4069 /// This is not the same as `struct {}`, since that is a struct rather than a tuple.
4070 empty_tuple_type,
4071
4072 /// `undefined` (untyped)
4073 undef,
4074 /// `@as(bool, undefined)`
4075 undef_bool,
4076 /// `@as(usize, undefined)`
4077 undef_usize,
4078 /// `@as(u1, undefined)`
4079 undef_u1,
4080 /// `0` (comptime_int)
4081 zero,
4082 /// `@as(usize, 0)`
4083 zero_usize,
4084 /// `@as(u1, 0)`
4085 zero_u1,
4086 /// `@as(u8, 0)`
4087 zero_u8,
4088 /// `1` (comptime_int)
4089 one,
4090 /// `@as(usize, 1)`
4091 one_usize,
4092 /// `@as(u1, 1)`
4093 one_u1,
4094 /// `@as(u8, 1)`
4095 one_u8,
4096 /// `@as(u8, 4)`
4097 four_u8,
4098 /// `-1` (comptime_int)
4099 negative_one,
4100 /// `{}`
4101 void_value,
4102 /// `unreachable` (noreturn type)
4103 unreachable_value,
4104 /// `null` (untyped)
4105 null_value,
4106 /// `true`
4107 bool_true,
4108 /// `false`
4109 bool_false,
4110 /// `.{}`
4111 empty_tuple,
4112
4113 /// Used by Air/Sema only.
4114 none = std.math.maxInt(u32),
4115
4116 _,
4117
4118 /// An array of `Index` existing within the `extra` array.
4119 /// This type exists to provide a struct with lifetime that is
4120 /// not invalidated when items are added to the `InternPool`.
4121 pub const Slice = struct {
4122 tid: Zcu.PerThread.Id,
4123 start: u32,
4124 len: u32,
4125
4126 pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 };
4127
4128 pub fn get(slice: Slice, ip: *const InternPool) []Index {
4129 const extra = ip.getLocalShared(slice.tid).extra.acquire();
4130 return @ptrCast(extra.view().items(.@"0")[slice.start..][0..slice.len]);
4131 }
4132
4133 /// If `slice` is empty (`slice.len == 0`), returns `.none`.
4134 /// Otherwise, asserts that `index < slice.len`, and returns the value at `index`.
4135 pub fn getOrNone(slice: Slice, ip: *const InternPool, index: usize) Index {
4136 if (slice.len == 0) return .none;
4137 return slice.get(ip)[index];
4138 }
4139 };
4140
4141 /// Used for a map of `Index` values to the index within a list of `Index` values.
4142 const Adapter = struct {
4143 indexes: []const Index,
4144
4145 pub fn eql(ctx: @This(), a: Index, b_void: void, b_map_index: usize) bool {
4146 _ = b_void;
4147 return a == ctx.indexes[b_map_index];
4148 }
4149
4150 pub fn hash(ctx: @This(), a: Index) u32 {
4151 _ = ctx;
4152 return std.hash.int(@backingInt(a));
4153 }
4154 };
4155
4156 const Unwrapped = struct {
4157 tid: Zcu.PerThread.Id,
4158 index: u32,
4159
4160 fn wrap(unwrapped: Unwrapped, ip: *const InternPool) Index {
4161 assert(@backingInt(unwrapped.tid) <= ip.getTidMask());
4162 assert(unwrapped.index <= ip.getIndexMask(u30));
4163 return @fromBackingInt(@intCast(@shlExact(@as(u32, @backingInt(unwrapped.tid)), ip.tid_shift_30) |
4164 unwrapped.index));
4165 }
4166
4167 pub fn getExtra(unwrapped: Unwrapped, ip: *const InternPool) Local.Extra {
4168 return ip.getLocalShared(unwrapped.tid).extra.acquire();
4169 }
4170
4171 pub fn getItem(unwrapped: Unwrapped, ip: *const InternPool) Item {
4172 const item_ptr = unwrapped.itemPtr(ip);
4173 const tag = @atomicLoad(Tag, item_ptr.tag_ptr, .acquire);
4174 return .{ .tag = tag, .data = item_ptr.data_ptr.* };
4175 }
4176
4177 pub fn getTag(unwrapped: Unwrapped, ip: *const InternPool) Tag {
4178 const item_ptr = unwrapped.itemPtr(ip);
4179 return @atomicLoad(Tag, item_ptr.tag_ptr, .acquire);
4180 }
4181
4182 pub fn getData(unwrapped: Unwrapped, ip: *const InternPool) u32 {
4183 return unwrapped.getItem(ip).data;
4184 }
4185
4186 const ItemPtr = struct {
4187 tag_ptr: *Tag,
4188 data_ptr: *u32,
4189 };
4190 fn itemPtr(unwrapped: Unwrapped, ip: *const InternPool) ItemPtr {
4191 const slice = ip.getLocalShared(unwrapped.tid).items.acquire().view().slice();
4192 return .{
4193 .tag_ptr = &slice.items(.tag)[unwrapped.index],
4194 .data_ptr = &slice.items(.data)[unwrapped.index],
4195 };
4196 }
4197
4198 const debug_state = InternPool.debug_state;
4199 };
4200 pub fn unwrap(index: Index, ip: *const InternPool) Unwrapped {
4201 return .{
4202 .tid = @fromBackingInt(@intCast(@backingInt(index) >> ip.tid_shift_30 & ip.getTidMask())),
4203 .index = @backingInt(index) & ip.getIndexMask(u30),
4204 };
4205 }
4206
4207 /// This function is used in the debugger pretty formatters in lib/lldb/ to fetch the
4208 /// Tag to encoding mapping to facilitate fancy debug printing for this type.
4209 fn dbHelper(self: *Index, tag_to_encoding_map: *struct {
4210 const DataIsIndex = struct { data: Index };
4211
4212 removed: void,
4213 type_int_signed: struct { data: u32 },
4214 type_int_unsigned: struct { data: u32 },
4215 type_array_big: struct { data: *Array },
4216 type_array_small: struct { data: *Vector },
4217 type_vector: struct { data: *Vector },
4218 type_pointer: struct { data: *Tag.TypePointer },
4219 type_slice: DataIsIndex,
4220 type_optional: DataIsIndex,
4221 type_anyframe: DataIsIndex,
4222 type_error_union: struct { data: *Key.ErrorUnionType },
4223 type_anyerror_union: DataIsIndex,
4224 type_error_set: struct {
4225 const @"data.names_len" = opaque {};
4226 data: *Tag.ErrorSet,
4227 @"trailing.names.len": *@"data.names_len",
4228 trailing: struct { names: []NullTerminatedString },
4229 },
4230 type_inferred_error_set: DataIsIndex,
4231 simple_type: void,
4232 type_function: struct {
4233 const @"data.params_len" = opaque {};
4234 data: *Tag.TypeFunction,
4235 @"trailing.param_types.len": *@"data.params_len",
4236 trailing: struct { param_types: []Index },
4237 },
4238 type_tuple: struct {
4239 const @"data.fields_len" = opaque {};
4240 data: *TypeTuple,
4241 @"trailing.types.len": *@"data.fields_len",
4242 @"trailing.values.len": *@"data.fields_len",
4243 trailing: struct { types: []Index, values: []Index },
4244 },
4245
4246 type_struct: struct { data: *Tag.TypeStruct },
4247 type_struct_packed_auto: struct { data: *Tag.TypeStructPacked },
4248 type_struct_packed_explicit: struct { data: *Tag.TypeStructPacked },
4249 type_struct_packed_auto_defaults: struct { data: *Tag.TypeStructPacked },
4250 type_struct_packed_explicit_defaults: struct { data: *Tag.TypeStructPacked },
4251 type_union: struct { data: *Tag.TypeUnion },
4252 type_union_packed_auto: struct { data: *Tag.TypeUnionPacked },
4253 type_union_packed_explicit: struct { data: *Tag.TypeUnionPacked },
4254 type_enum_auto: struct { data: *Tag.TypeEnum },
4255 type_enum_explicit: struct { data: *Tag.TypeEnum },
4256 type_enum_nonexhaustive: struct { data: *Tag.TypeEnum },
4257 type_opaque: struct { data: *Tag.TypeOpaque },
4258
4259 type_spirv: struct { data: *Tag.TypeSpirv },
4260
4261 undef: DataIsIndex,
4262 simple_value: void,
4263 ptr_nav: struct { data: *PtrNav },
4264 ptr_comptime_alloc: struct { data: *PtrComptimeAlloc },
4265 ptr_uav: struct { data: *PtrUav },
4266 ptr_uav_aligned: struct { data: *PtrUavAligned },
4267 ptr_comptime_field: struct { data: *PtrComptimeField },
4268 ptr_int: struct { data: *PtrInt },
4269 ptr_eu_payload: struct { data: *PtrBase },
4270 ptr_opt_payload: struct { data: *PtrBase },
4271 ptr_elem: struct { data: *PtrBaseIndex },
4272 ptr_field: struct { data: *PtrBaseIndex },
4273 ptr_slice: struct { data: *PtrSlice },
4274 opt_payload: struct { data: *Tag.TypeValue },
4275 opt_null: DataIsIndex,
4276 int_u8: struct { data: u8 },
4277 int_u16: struct { data: u16 },
4278 int_u32: struct { data: u32 },
4279 int_i32: struct { data: i32 },
4280 int_usize: struct { data: u32 },
4281 int_comptime_int_u32: struct { data: u32 },
4282 int_comptime_int_i32: struct { data: i32 },
4283 int_small: struct { data: *IntSmall },
4284 int_positive: struct { data: u32 },
4285 int_negative: struct { data: u32 },
4286 error_set_error: struct { data: *Key.Error },
4287 error_union_error: struct { data: *Key.Error },
4288 error_union_payload: struct { data: *Tag.TypeValue },
4289 enum_literal: struct { data: NullTerminatedString },
4290 enum_tag: struct { data: *Tag.EnumTag },
4291 float_f16: struct { data: f16 },
4292 float_f32: struct { data: f32 },
4293 float_f64: struct { data: *Float64 },
4294 float_f80: struct { data: *Float80 },
4295 float_f128: struct { data: *Float128 },
4296 float_c_longdouble_f80: struct { data: *Float80 },
4297 float_c_longdouble_f128: struct { data: *Float128 },
4298 float_comptime_float: struct { data: *Float128 },
4299 @"extern": struct { data: *Tag.Extern },
4300 func_decl: struct {
4301 const @"data.analysis.inferred_error_set" = opaque {};
4302 data: *Tag.FuncDecl,
4303 @"trailing.resolved_error_set.len": *@"data.analysis.inferred_error_set",
4304 trailing: struct { resolved_error_set: []Index },
4305 },
4306 func_instance: struct {
4307 const @"data.analysis.inferred_error_set" = opaque {};
4308 const @"data.generic_owner.data.ty.data.params_len" = opaque {};
4309 data: *Tag.FuncInstance,
4310 @"trailing.resolved_error_set.len": *@"data.analysis.inferred_error_set",
4311 @"trailing.comptime_args.len": *@"data.generic_owner.data.ty.data.params_len",
4312 trailing: struct { resolved_error_set: []Index, comptime_args: []Index },
4313 },
4314 func_coerced: struct {
4315 data: *Tag.FuncCoerced,
4316 },
4317 only_possible_value: DataIsIndex,
4318 union_value: struct { data: *Key.Union },
4319 bytes: struct { data: *Bytes },
4320 aggregate: struct {
4321 const @"data.ty.data.len orelse data.ty.data.fields_len" = opaque {};
4322 data: *Tag.Aggregate,
4323 @"trailing.element_values.len": *@"data.ty.data.len orelse data.ty.data.fields_len",
4324 trailing: struct { element_values: []Index },
4325 },
4326 repeated: struct { data: *Repeated },
4327 bitpack: struct { data: *Key.Bitpack },
4328
4329 memoized_call: struct {
4330 const @"data.args_len" = opaque {};
4331 data: *MemoizedCall,
4332 @"trailing.arg_values.len": *@"data.args_len",
4333 trailing: struct { arg_values: []Index },
4334 },
4335 }) void {
4336 _ = self;
4337 const map_info = @typeInfo(@typeInfo(@TypeOf(tag_to_encoding_map)).pointer.child).@"struct";
4338 @setEvalBranchQuota(3_000);
4339 inline for (@typeInfo(Tag).@"enum".field_names, 0..) |tag_name, start| {
4340 inline for (0..map_info.field_names.len) |offset| {
4341 if (comptime std.mem.eql(u8, tag_name, map_info.field_names[(start + offset) % map_info.field_names.len])) break;
4342 } else {
4343 @compileError(@typeName(Tag) ++ "." ++ tag_name ++ " missing dbHelper tag_to_encoding_map entry");
4344 }
4345 }
4346 }
4347 comptime {
4348 if (!builtin.strip_debug_info) switch (builtin.zig_backend) {
4349 .stage2_llvm => _ = &dbHelper,
4350 .stage2_x86_64 => for (@typeInfo(Tag).@"enum".field_names) |tag_name| {
4351 if (!@hasField(@TypeOf(Tag.encodings), tag_name)) @compileLog("missing: " ++ @typeName(Tag) ++ ".encodings." ++ tag_name);
4352 const encoding = @field(Tag.encodings, tag_name);
4353 if (@hasField(@TypeOf(encoding), "trailing")) {
4354 const trailing_info = @typeInfo(encoding.trailing).@"struct";
4355 for (trailing_info.field_names, trailing_info.field_types) |trailing_field_name, trailing_field_type| {
4356 struct {
4357 fn checkConfig(name: []const u8) void {
4358 if (!@hasField(@TypeOf(encoding.config), name)) @compileError("missing field: " ++ @typeName(Tag) ++ ".encodings." ++ tag_name ++ ".config.@\"" ++ name ++ "\"");
4359 const FieldType = @TypeOf(@field(encoding.config, name));
4360 if (@typeInfo(FieldType) != .enum_literal) @compileError("expected enum literal: " ++ @typeName(Tag) ++ ".encodings." ++ tag_name ++ ".config.@\"" ++ name ++ "\": " ++ @typeName(FieldType));
4361 }
4362 fn checkField(name: []const u8, Type: type) void {
4363 switch (@typeInfo(Type)) {
4364 .int, .@"enum" => return,
4365 .@"struct" => |info| switch (info.layout) {
4366 .auto => unreachable,
4367 .@"extern" => {
4368 for (info.field_names, info.field_types) |field_name, field_type| checkField(name ++ "." ++ field_name, field_type);
4369 return;
4370 },
4371 .@"packed" => return,
4372 },
4373 .optional => |info| {
4374 checkConfig(name ++ ".?");
4375 checkField(name ++ ".?", info.child);
4376 return;
4377 },
4378 .pointer => |info| if (info.size == .slice) {
4379 checkConfig(name ++ ".len");
4380 checkField(name ++ "[0]", info.child);
4381 return;
4382 },
4383 else => {},
4384 }
4385 @compileError("unsupported type: " ++ @typeName(Tag) ++ ".encodings." ++ tag_name ++ "." ++ name ++ ": " ++ @typeName(Type));
4386 }
4387 }.checkField("trailing." ++ trailing_field_name, trailing_field_type);
4388 }
4389 }
4390 },
4391 else => {},
4392 };
4393 }
4394};
4395
4396pub const static_keys: [static_len]Key = .{
4397 .{ .int_type = .{
4398 .signedness = .unsigned,
4399 .bits = 0,
4400 } },
4401
4402 .{ .int_type = .{
4403 .signedness = .unsigned,
4404 .bits = 1,
4405 } },
4406
4407 .{ .int_type = .{
4408 .signedness = .unsigned,
4409 .bits = 8,
4410 } },
4411
4412 .{ .int_type = .{
4413 .signedness = .signed,
4414 .bits = 8,
4415 } },
4416
4417 .{ .int_type = .{
4418 .signedness = .unsigned,
4419 .bits = 16,
4420 } },
4421
4422 .{ .int_type = .{
4423 .signedness = .signed,
4424 .bits = 16,
4425 } },
4426
4427 .{ .int_type = .{
4428 .signedness = .unsigned,
4429 .bits = 29,
4430 } },
4431
4432 .{ .int_type = .{
4433 .signedness = .unsigned,
4434 .bits = 32,
4435 } },
4436
4437 .{ .int_type = .{
4438 .signedness = .signed,
4439 .bits = 32,
4440 } },
4441
4442 .{ .int_type = .{
4443 .signedness = .unsigned,
4444 .bits = 64,
4445 } },
4446
4447 .{ .int_type = .{
4448 .signedness = .signed,
4449 .bits = 64,
4450 } },
4451
4452 .{ .int_type = .{
4453 .signedness = .unsigned,
4454 .bits = 80,
4455 } },
4456
4457 .{ .int_type = .{
4458 .signedness = .unsigned,
4459 .bits = 128,
4460 } },
4461
4462 .{ .int_type = .{
4463 .signedness = .signed,
4464 .bits = 128,
4465 } },
4466
4467 .{ .int_type = .{
4468 .signedness = .unsigned,
4469 .bits = 256,
4470 } },
4471
4472 .{ .simple_type = .usize },
4473 .{ .simple_type = .isize },
4474 .{ .simple_type = .c_char },
4475 .{ .simple_type = .c_short },
4476 .{ .simple_type = .c_ushort },
4477 .{ .simple_type = .c_int },
4478 .{ .simple_type = .c_uint },
4479 .{ .simple_type = .c_long },
4480 .{ .simple_type = .c_ulong },
4481 .{ .simple_type = .c_longlong },
4482 .{ .simple_type = .c_ulonglong },
4483 .{ .simple_type = .c_longdouble },
4484 .{ .simple_type = .f16 },
4485 .{ .simple_type = .f32 },
4486 .{ .simple_type = .f64 },
4487 .{ .simple_type = .f80 },
4488 .{ .simple_type = .f128 },
4489 .{ .simple_type = .anyopaque },
4490 .{ .simple_type = .bool },
4491 .{ .simple_type = .void },
4492 .{ .simple_type = .type },
4493 .{ .simple_type = .anyerror },
4494 .{ .simple_type = .comptime_int },
4495 .{ .simple_type = .comptime_float },
4496 .{ .simple_type = .noreturn },
4497 .{ .anyframe_type = .none },
4498 .{ .simple_type = .null },
4499 .{ .simple_type = .undefined },
4500 .{ .simple_type = .enum_literal },
4501
4502 // *usize
4503 .{ .ptr_type = .{
4504 .child = .usize_type,
4505 .flags = .{},
4506 } },
4507
4508 // *const comptime_int
4509 .{ .ptr_type = .{
4510 .child = .comptime_int_type,
4511 .flags = .{
4512 .is_const = true,
4513 },
4514 } },
4515
4516 // [*]u8
4517 .{ .ptr_type = .{
4518 .child = .u8_type,
4519 .flags = .{
4520 .size = .many,
4521 },
4522 } },
4523
4524 // [*]const u8
4525 .{ .ptr_type = .{
4526 .child = .u8_type,
4527 .flags = .{
4528 .size = .many,
4529 .is_const = true,
4530 },
4531 } },
4532
4533 // [*:0]const u8
4534 .{ .ptr_type = .{
4535 .child = .u8_type,
4536 .sentinel = .zero_u8,
4537 .flags = .{
4538 .size = .many,
4539 .is_const = true,
4540 },
4541 } },
4542
4543 // []const u8
4544 .{ .ptr_type = .{
4545 .child = .u8_type,
4546 .flags = .{
4547 .size = .slice,
4548 .is_const = true,
4549 },
4550 } },
4551
4552 // [:0]const u8
4553 .{ .ptr_type = .{
4554 .child = .u8_type,
4555 .sentinel = .zero_u8,
4556 .flags = .{
4557 .size = .slice,
4558 .is_const = true,
4559 },
4560 } },
4561
4562 // [*]const []const u8
4563 .{ .ptr_type = .{
4564 .child = .slice_const_u8_type,
4565 .flags = .{
4566 .size = .many,
4567 .is_const = true,
4568 },
4569 } },
4570
4571 // []const []const u8
4572 .{ .ptr_type = .{
4573 .child = .slice_const_u8_type,
4574 .flags = .{
4575 .size = .slice,
4576 .is_const = true,
4577 },
4578 } },
4579
4580 // ?type
4581 .{ .opt_type = .type_type },
4582
4583 // [*]const type
4584 .{ .ptr_type = .{
4585 .child = .type_type,
4586 .flags = .{
4587 .size = .many,
4588 .is_const = true,
4589 },
4590 } },
4591
4592 // []const type
4593 .{ .ptr_type = .{
4594 .child = .type_type,
4595 .flags = .{
4596 .size = .slice,
4597 .is_const = true,
4598 },
4599 } },
4600
4601 // @Vector(8, i8)
4602 .{ .vector_type = .{ .len = 8, .child = .i8_type } },
4603 // @Vector(16, i8)
4604 .{ .vector_type = .{ .len = 16, .child = .i8_type } },
4605 // @Vector(32, i8)
4606 .{ .vector_type = .{ .len = 32, .child = .i8_type } },
4607 // @Vector(64, i8)
4608 .{ .vector_type = .{ .len = 64, .child = .i8_type } },
4609 // @Vector(1, u8)
4610 .{ .vector_type = .{ .len = 1, .child = .u8_type } },
4611 // @Vector(2, u8)
4612 .{ .vector_type = .{ .len = 2, .child = .u8_type } },
4613 // @Vector(4, u8)
4614 .{ .vector_type = .{ .len = 4, .child = .u8_type } },
4615 // @Vector(8, u8)
4616 .{ .vector_type = .{ .len = 8, .child = .u8_type } },
4617 // @Vector(16, u8)
4618 .{ .vector_type = .{ .len = 16, .child = .u8_type } },
4619 // @Vector(32, u8)
4620 .{ .vector_type = .{ .len = 32, .child = .u8_type } },
4621 // @Vector(64, u8)
4622 .{ .vector_type = .{ .len = 64, .child = .u8_type } },
4623 // @Vector(2, i16)
4624 .{ .vector_type = .{ .len = 2, .child = .i16_type } },
4625 // @Vector(4, i16)
4626 .{ .vector_type = .{ .len = 4, .child = .i16_type } },
4627 // @Vector(8, i16)
4628 .{ .vector_type = .{ .len = 8, .child = .i16_type } },
4629 // @Vector(16, i16)
4630 .{ .vector_type = .{ .len = 16, .child = .i16_type } },
4631 // @Vector(32, i16)
4632 .{ .vector_type = .{ .len = 32, .child = .i16_type } },
4633 // @Vector(4, u16)
4634 .{ .vector_type = .{ .len = 4, .child = .u16_type } },
4635 // @Vector(8, u16)
4636 .{ .vector_type = .{ .len = 8, .child = .u16_type } },
4637 // @Vector(16, u16)
4638 .{ .vector_type = .{ .len = 16, .child = .u16_type } },
4639 // @Vector(32, u16)
4640 .{ .vector_type = .{ .len = 32, .child = .u16_type } },
4641 // @Vector(2, i32)
4642 .{ .vector_type = .{ .len = 2, .child = .i32_type } },
4643 // @Vector(4, i32)
4644 .{ .vector_type = .{ .len = 4, .child = .i32_type } },
4645 // @Vector(8, i32)
4646 .{ .vector_type = .{ .len = 8, .child = .i32_type } },
4647 // @Vector(16, i32)
4648 .{ .vector_type = .{ .len = 16, .child = .i32_type } },
4649 // @Vector(4, u32)
4650 .{ .vector_type = .{ .len = 4, .child = .u32_type } },
4651 // @Vector(8, u32)
4652 .{ .vector_type = .{ .len = 8, .child = .u32_type } },
4653 // @Vector(16, u32)
4654 .{ .vector_type = .{ .len = 16, .child = .u32_type } },
4655 // @Vector(2, i64)
4656 .{ .vector_type = .{ .len = 2, .child = .i64_type } },
4657 // @Vector(4, i64)
4658 .{ .vector_type = .{ .len = 4, .child = .i64_type } },
4659 // @Vector(8, i64)
4660 .{ .vector_type = .{ .len = 8, .child = .i64_type } },
4661 // @Vector(2, u64)
4662 .{ .vector_type = .{ .len = 2, .child = .u64_type } },
4663 // @Vector(4, u64)
4664 .{ .vector_type = .{ .len = 4, .child = .u64_type } },
4665 // @Vector(8, u64)
4666 .{ .vector_type = .{ .len = 8, .child = .u64_type } },
4667 // @Vector(1, u128)
4668 .{ .vector_type = .{ .len = 1, .child = .u128_type } },
4669 // @Vector(2, u128)
4670 .{ .vector_type = .{ .len = 2, .child = .u128_type } },
4671 // @Vector(1, u256)
4672 .{ .vector_type = .{ .len = 1, .child = .u256_type } },
4673 // @Vector(4, f16)
4674 .{ .vector_type = .{ .len = 4, .child = .f16_type } },
4675 // @Vector(8, f16)
4676 .{ .vector_type = .{ .len = 8, .child = .f16_type } },
4677 // @Vector(16, f16)
4678 .{ .vector_type = .{ .len = 16, .child = .f16_type } },
4679 // @Vector(32, f16)
4680 .{ .vector_type = .{ .len = 32, .child = .f16_type } },
4681 // @Vector(2, f32)
4682 .{ .vector_type = .{ .len = 2, .child = .f32_type } },
4683 // @Vector(4, f32)
4684 .{ .vector_type = .{ .len = 4, .child = .f32_type } },
4685 // @Vector(8, f32)
4686 .{ .vector_type = .{ .len = 8, .child = .f32_type } },
4687 // @Vector(16, f32)
4688 .{ .vector_type = .{ .len = 16, .child = .f32_type } },
4689 // @Vector(2, f64)
4690 .{ .vector_type = .{ .len = 2, .child = .f64_type } },
4691 // @Vector(4, f64)
4692 .{ .vector_type = .{ .len = 4, .child = .f64_type } },
4693 // @Vector(8, f64)
4694 .{ .vector_type = .{ .len = 8, .child = .f64_type } },
4695
4696 // ?noreturn
4697 .{ .opt_type = .noreturn_type },
4698
4699 // anyerror!void
4700 .{ .error_union_type = .{
4701 .error_set_type = .anyerror_type,
4702 .payload_type = .void_type,
4703 } },
4704
4705 // adhoc_inferred_error_set_type
4706 .{ .simple_type = .adhoc_inferred_error_set },
4707 // generic_poison_type
4708 .{ .simple_type = .generic_poison },
4709
4710 // empty_tuple_type
4711 .{ .tuple_type = .{
4712 .types = .empty,
4713 .values = .empty,
4714 } },
4715
4716 .{ .undef = .undefined_type },
4717 .{ .undef = .bool_type },
4718 .{ .undef = .usize_type },
4719 .{ .undef = .u1_type },
4720
4721 .{ .int = .{
4722 .ty = .comptime_int_type,
4723 .storage = .{ .u64 = 0 },
4724 } },
4725
4726 .{ .int = .{
4727 .ty = .usize_type,
4728 .storage = .{ .u64 = 0 },
4729 } },
4730
4731 .{ .int = .{
4732 .ty = .u1_type,
4733 .storage = .{ .u64 = 0 },
4734 } },
4735
4736 .{ .int = .{
4737 .ty = .u8_type,
4738 .storage = .{ .u64 = 0 },
4739 } },
4740
4741 .{ .int = .{
4742 .ty = .comptime_int_type,
4743 .storage = .{ .u64 = 1 },
4744 } },
4745
4746 .{ .int = .{
4747 .ty = .usize_type,
4748 .storage = .{ .u64 = 1 },
4749 } },
4750
4751 .{ .int = .{
4752 .ty = .u1_type,
4753 .storage = .{ .u64 = 1 },
4754 } },
4755
4756 .{ .int = .{
4757 .ty = .u8_type,
4758 .storage = .{ .u64 = 1 },
4759 } },
4760
4761 .{ .int = .{
4762 .ty = .u8_type,
4763 .storage = .{ .u64 = 4 },
4764 } },
4765
4766 .{ .int = .{
4767 .ty = .comptime_int_type,
4768 .storage = .{ .i64 = -1 },
4769 } },
4770
4771 .{ .simple_value = .void },
4772 .{ .simple_value = .@"unreachable" },
4773 .{ .simple_value = .null },
4774 .{ .simple_value = .true },
4775 .{ .simple_value = .false },
4776
4777 .{ .aggregate = .{
4778 .ty = .empty_tuple_type,
4779 .storage = .{ .elems = &.{} },
4780 } },
4781};
4782
4783/// How many items in the InternPool are statically known.
4784/// This is specified with an integer literal and a corresponding comptime
4785/// assert below to break an unfortunate and arguably incorrect dependency loop
4786/// when compiling.
4787pub const static_len = Zir.Inst.Ref.static_len;
4788
4789pub const Tag = enum(u8) {
4790 /// This special tag represents a value which was removed from this pool via
4791 /// `InternPool.remove`. The item remains allocated to preserve indices, but
4792 /// lookups will consider it not equal to any other item, and all queries
4793 /// assert not this tag. `data` is unused.
4794 removed,
4795
4796 /// A type that can be represented with only an enum tag.
4797 simple_type,
4798 /// An integer type.
4799 /// data is number of bits
4800 type_int_signed,
4801 /// An integer type.
4802 /// data is number of bits
4803 type_int_unsigned,
4804 /// An array type whose length requires 64 bits or which has a sentinel.
4805 /// data is payload to Array.
4806 type_array_big,
4807 /// An array type that has no sentinel and whose length fits in 32 bits.
4808 /// data is payload to Vector.
4809 type_array_small,
4810 /// A vector type.
4811 /// data is payload to Vector.
4812 type_vector,
4813 /// A fully explicitly specified pointer type.
4814 type_pointer,
4815 /// A slice type.
4816 /// data is Index of underlying pointer type.
4817 type_slice,
4818 /// An optional type.
4819 /// data is the child type.
4820 type_optional,
4821 /// The type `anyframe->T`.
4822 /// data is the child type.
4823 /// If the child type is `none`, the type is `anyframe`.
4824 type_anyframe,
4825 /// An error union type.
4826 /// data is payload to `Key.ErrorUnionType`.
4827 type_error_union,
4828 /// An error union type of the form `anyerror!T`.
4829 /// data is `Index` of payload type.
4830 type_anyerror_union,
4831 /// An error set type.
4832 /// data is payload to `ErrorSet`.
4833 type_error_set,
4834 /// The inferred error set type of a function.
4835 /// data is `Index` of a `func_decl` or `func_instance`.
4836 type_inferred_error_set,
4837 /// A function body type.
4838 /// `data` is extra index to `TypeFunction`.
4839 type_function,
4840 /// A `TupleType`.
4841 /// data is extra index of `TypeTuple`.
4842 type_tuple,
4843
4844 /// A non-packed struct type.
4845 /// data is extra index of `TypeStruct`.
4846 type_struct,
4847 /// `packed struct { ... }` with no default field values.
4848 /// data is extra index of `TypeStructPacked`.
4849 type_struct_packed_auto,
4850 /// `packed struct(T) { ... }` with no default field values.
4851 /// data is extra index of `TypeStructPacked`.
4852 type_struct_packed_explicit,
4853 /// `packed struct { ... }` with one or more default field values.
4854 /// data is extra index of `TypeStructPacked`.
4855 type_struct_packed_auto_defaults,
4856 /// `packed struct(T) { ... }` with one or more default field values.
4857 /// data is extra index of `TypeStructPacked`.
4858 type_struct_packed_explicit_defaults,
4859
4860 /// A non-packed union type.
4861 /// data is extra index of `TypeUnion`.
4862 type_union,
4863 /// `packed union { ... }`.
4864 /// data is extra index of `TypeUnionPacked`.
4865 type_union_packed_auto,
4866 /// `packed union(T) { ... }`.
4867 /// data is extra index of `TypeUnionPacked`.
4868 type_union_packed_explicit,
4869
4870 /// An exhaustive enum type *without* an explicit integer tag type. The tag type is inferred.
4871 ///
4872 /// Because the tag type is inferred, there are no explicit field values.
4873 ///
4874 /// May be the generated tag type for a `union(enum)`.
4875 ///
4876 /// data is extra index of `TypeEnum`.
4877 type_enum_auto,
4878 /// An exhaustive enum type *with* an explicit integer tag type.
4879 ///
4880 /// May have explicit field values.
4881 ///
4882 /// May be the generated tag type for a `union(enum(T))`.
4883 ///
4884 /// data is extra index of `TypeEnum`.
4885 type_enum_explicit,
4886 /// An non-exhaustive enum type (with an explicit integer tag type, since it is required for
4887 /// non-exhaustive enums).
4888 ///
4889 /// May have explicit field values.
4890 ///
4891 /// This is *not* a union's generated tag type, because such types are always exhaustive.
4892 ///
4893 /// data is extra index of `TypeEnum`.
4894 type_enum_nonexhaustive,
4895
4896 /// An spirv type.
4897 /// data is index of `TypeSpirv` in extra.
4898 type_spirv,
4899
4900 /// An opaque type.
4901 /// data is extra index of `TypeOpaque`.
4902 type_opaque,
4903
4904 /// Typed `undefined`.
4905 /// `data` is `Index` of the type.
4906 /// Untyped `undefined` is stored instead via `simple_value`.
4907 undef,
4908 /// A value that can be represented with only an enum tag.
4909 simple_value,
4910 /// A pointer to a `Nav`.
4911 /// data is extra index of `PtrNav`, which contains the type and address.
4912 ptr_nav,
4913 /// A pointer to a decl that can be mutated at comptime.
4914 /// data is extra index of `PtrComptimeAlloc`, which contains the type and address.
4915 ptr_comptime_alloc,
4916 /// A pointer to an anonymous addressable value.
4917 /// data is extra index of `PtrUav`, which contains the pointer type and decl value.
4918 /// The alignment of the uav is communicated via the pointer type.
4919 ptr_uav,
4920 /// A pointer to an unnamed addressable value.
4921 /// data is extra index of `PtrUavAligned`, which contains the pointer
4922 /// type and decl value.
4923 /// The original pointer type is also provided, which will be different than `ty`.
4924 /// This encoding is only used when a pointer to a Uav is
4925 /// coerced to a different pointer type with a different alignment.
4926 ptr_uav_aligned,
4927 /// data is extra index of `PtrComptimeField`, which contains the pointer type and field value.
4928 ptr_comptime_field,
4929 /// A pointer with an integer value.
4930 /// data is extra index of `PtrInt`, which contains the type and address (byte offset from 0).
4931 /// Only pointer types are allowed to have this encoding. Optional types must use
4932 /// `opt_payload` or `opt_null`.
4933 ptr_int,
4934 /// A pointer to the payload of an error union.
4935 /// data is extra index of `PtrBase`, which contains the type and base pointer.
4936 ptr_eu_payload,
4937 /// A pointer to the payload of an optional.
4938 /// data is extra index of `PtrBase`, which contains the type and base pointer.
4939 ptr_opt_payload,
4940 /// A pointer to an array element.
4941 /// data is extra index of PtrBaseIndex, which contains the base array and element index.
4942 /// In order to use this encoding, one must ensure that the `InternPool`
4943 /// already contains the elem pointer type corresponding to this payload.
4944 ptr_elem,
4945 /// A pointer to a container field.
4946 /// data is extra index of PtrBaseIndex, which contains the base container and field index.
4947 ptr_field,
4948 /// A slice.
4949 /// data is extra index of PtrSlice, which contains the ptr and len values
4950 ptr_slice,
4951 /// An optional value that is non-null.
4952 /// data is extra index of `TypeValue`.
4953 /// The type is the optional type (not the payload type).
4954 opt_payload,
4955 /// An optional value that is null.
4956 /// data is Index of the optional type.
4957 opt_null,
4958 /// Type: u8
4959 /// data is integer value
4960 int_u8,
4961 /// Type: u16
4962 /// data is integer value
4963 int_u16,
4964 /// Type: u32
4965 /// data is integer value
4966 int_u32,
4967 /// Type: i32
4968 /// data is integer value bitcasted to u32.
4969 int_i32,
4970 /// A usize that fits in 32 bits.
4971 /// data is integer value.
4972 int_usize,
4973 /// A comptime_int that fits in a u32.
4974 /// data is integer value.
4975 int_comptime_int_u32,
4976 /// A comptime_int that fits in an i32.
4977 /// data is integer value bitcasted to u32.
4978 int_comptime_int_i32,
4979 /// An integer value that fits in 32 bits with an explicitly provided type.
4980 /// data is extra index of `IntSmall`.
4981 int_small,
4982 /// A positive integer value.
4983 /// data is a limbs index to `Int`.
4984 int_positive,
4985 /// A negative integer value.
4986 /// data is a limbs index to `Int`.
4987 int_negative,
4988 /// An error value.
4989 /// data is extra index of `Key.Error`.
4990 error_set_error,
4991 /// An error union error.
4992 /// data is extra index of `Key.Error`.
4993 error_union_error,
4994 /// An error union payload.
4995 /// data is extra index of `TypeValue`.
4996 error_union_payload,
4997 /// An enum literal value.
4998 /// data is `NullTerminatedString` of the error name.
4999 enum_literal,
5000 /// An enum tag value.
5001 /// data is extra index of `EnumTag`.
5002 enum_tag,
5003 /// An f16 value.
5004 /// data is float value bitcasted to u16 and zero-extended.
5005 float_f16,
5006 /// An f32 value.
5007 /// data is float value bitcasted to u32.
5008 float_f32,
5009 /// An f64 value.
5010 /// data is extra index to Float64.
5011 float_f64,
5012 /// An f80 value.
5013 /// data is extra index to Float80.
5014 float_f80,
5015 /// An f128 value.
5016 /// data is extra index to Float128.
5017 float_f128,
5018 /// A c_longdouble value of 80 bits.
5019 /// data is extra index to Float80.
5020 /// This is used when a c_longdouble value is provided as an f80, because f80 has unnormalized
5021 /// values which cannot be losslessly represented as f128. It should only be used when the type
5022 /// underlying c_longdouble for the target is 80 bits.
5023 float_c_longdouble_f80,
5024 /// A c_longdouble value of 128 bits.
5025 /// data is extra index to Float128.
5026 /// This is used when a c_longdouble value is provided as any type other than an f80, since all
5027 /// other float types can be losslessly converted to and from f128.
5028 float_c_longdouble_f128,
5029 /// A comptime_float value.
5030 /// data is extra index to Float128.
5031 float_comptime_float,
5032 /// An extern function or variable.
5033 /// data is extra index to Extern.
5034 /// Some parts of the key are stored in `owner_nav`.
5035 @"extern",
5036 /// A non-extern function corresponding directly to the AST node from whence it originated.
5037 /// data is extra index to `FuncDecl`.
5038 /// Only the owner Decl is used for hashing and equality because the other
5039 /// fields can get patched up during incremental compilation.
5040 func_decl,
5041 /// A generic function instantiation.
5042 /// data is extra index to `FuncInstance`.
5043 func_instance,
5044 /// A `func_decl` or a `func_instance` that has been coerced to a different type.
5045 /// data is extra index to `FuncCoerced`.
5046 func_coerced,
5047 /// This represents the only possible value for *some* types which have
5048 /// only one possible value. Not all only-possible-values are encoded this way;
5049 /// for example structs which have all comptime fields are not encoded this way.
5050 /// The set of values that are encoded this way is:
5051 /// * An array or vector which has length 0.
5052 /// * A struct which has all fields comptime-known.
5053 /// data is Index of the type, which is known to be zero bits at runtime.
5054 only_possible_value,
5055 /// data is extra index to Key.Union.
5056 union_value,
5057 /// An array of bytes.
5058 /// data is extra index to `Bytes`.
5059 bytes,
5060 /// An instance of a struct, array, or vector.
5061 /// data is extra index to `Aggregate`.
5062 aggregate,
5063 /// An instance of an array or vector with every element being the same value.
5064 /// data is extra index to `Repeated`.
5065 repeated,
5066 /// An instance of a `packed struct` or `packed union`.
5067 /// data is extra index to `Key.Bitpack`.
5068 bitpack,
5069
5070 /// A memoized comptime function call result.
5071 /// data is extra index to `MemoizedCall`
5072 memoized_call,
5073
5074 const ErrorUnionType = Key.ErrorUnionType;
5075 const TypeValue = Key.TypeValue;
5076 const Error = Key.Error;
5077 const EnumTag = Key.EnumTag;
5078 const Union = Key.Union;
5079 const TypePointer = Key.PtrType;
5080 const TypeSpirv = Key.SpirvType;
5081
5082 const struct_packed_encoding = .{
5083 .summary = .@"{.payload.name%summary#\"}",
5084 .payload = TypeStructPacked,
5085 .trailing = struct {
5086 type_hash: ?u64,
5087 captures: ?[]CaptureValue,
5088 field_names: []NullTerminatedString,
5089 field_types: []Index,
5090 },
5091 .config = .{
5092 .@"trailing.type_hash.?" = .@"payload.bits.captures_len == .reified",
5093 .@"trailing.captures.?" = .@"payload.bits.captures_len != .reified",
5094 .@"trailing.captures.?.len" = .@"@intFromEnum(payload.bits.captures_len)",
5095 .@"trailing.field_names.len" = .@"payload.fields_len",
5096 .@"trailing.field_types.len" = .@"payload.fields_len",
5097 },
5098 };
5099 const struct_packed_defaults_encoding = .{
5100 .summary = .@"{.payload.name%summary#\"}",
5101 .payload = TypeStructPacked,
5102 .trailing = struct {
5103 type_hash: ?u64,
5104 captures: ?[]CaptureValue,
5105 field_names: []NullTerminatedString,
5106 field_types: []Index,
5107 field_defaults: []Index,
5108 },
5109 .config = .{
5110 .@"trailing.type_hash.?" = .@"payload.bits.captures_len == .reified",
5111 .@"trailing.captures.?" = .@"payload.bits.captures_len != .reified",
5112 .@"trailing.captures.?.len" = .@"@intFromEnum(payload.bits.captures_len)",
5113 .@"trailing.field_names.len" = .@"payload.fields_len",
5114 .@"trailing.field_types.len" = .@"payload.fields_len",
5115 .@"trailing.field_defaults.len" = .@"payload.fields_len",
5116 },
5117 };
5118 const union_packed_encoding = .{
5119 .summary = .@"{.payload.name%summary#\"}",
5120 .payload = TypeUnionPacked,
5121 .trailing = struct {
5122 type_hash: ?u64,
5123 captures: ?[]CaptureValue,
5124 field_types: []Index,
5125 },
5126 .config = .{
5127 .@"trailing.type_hash.?" = .@"payload.bits.captures_len == .reified",
5128 .@"trailing.captures.?" = .@"payload.bits.captures_len != .reified",
5129 .@"trailing.captures.?.len" = .@"@intFromEnum(payload.bits.captures_len)",
5130 .@"trailing.field_types.len" = .@"payload.fields_len",
5131 },
5132 };
5133 const enum_explicit_encoding = .{
5134 .summary = .@"{.payload.name%summary#\"}",
5135 .payload = TypeEnum,
5136 .trailing = struct {
5137 owner_union: ?Index,
5138 zir_index: ?TrackedInst.Index,
5139 type_hash: ?u64,
5140 captures: ?[]CaptureValue,
5141 field_value_map: MapIndex,
5142 field_names: []NullTerminatedString,
5143 field_values: []Index,
5144 },
5145 .config = .{
5146 .@"trailing.owner_union.?" = .@"payload.bits.captures_len == .generated_union_tag",
5147 .@"trailing.zir_index.?" = .@"payload.bits.captures_len != .generated_union_tag",
5148 .@"trailing.type_hash.?" = .@"payload.bits.captures_len == .reified",
5149 .@"trailing.captures.?" = .@"payload.bits.captures_len != .reified and payload.bits.captures_len != .generated_union_tag",
5150 .@"trailing.captures.?.len" = .@"@intFromEnum(payload.bits.captures_len)",
5151 .@"trailing.field_names.len" = .@"payload.fields_len",
5152 .@"trailing.field_values.len" = .@"payload.fields_len",
5153 },
5154 };
5155 const encodings = .{
5156 .removed = .{},
5157
5158 .type_int_signed = .{ .summary = .@"i{.data%value}", .data = u32 },
5159 .type_int_unsigned = .{ .summary = .@"u{.data%value}", .data = u32 },
5160 .type_array_big = .{
5161 .summary = .@"[{.payload.len1%value} << 32 | {.payload.len0%value}:{.payload.sentinel%summary}]{.payload.child%summary}",
5162 .payload = Array,
5163 },
5164 .type_array_small = .{ .summary = .@"[{.payload.len%value}]{.payload.child%summary}", .payload = Vector },
5165 .type_vector = .{ .summary = .@"@Vector({.payload.len%value}, {.payload.child%summary})", .payload = Vector },
5166 .type_pointer = .{ .summary = .@"*... {.payload.child%summary}", .payload = TypePointer },
5167 .type_slice = .{ .summary = .@"[]... {.data.unwrapped.payload.child%summary}", .data = Index },
5168 .type_optional = .{ .summary = .@"?{.data%summary}", .data = Index },
5169 .type_anyframe = .{ .summary = .@"anyframe->{.data%summary}", .data = Index },
5170 .type_error_union = .{
5171 .summary = .@"{.payload.error_set_type%summary}!{.payload.payload_type%summary}",
5172 .payload = ErrorUnionType,
5173 },
5174 .type_anyerror_union = .{ .summary = .@"anyerror!{.data%summary}", .data = Index },
5175 .type_error_set = .{ .summary = .@"error{...}", .payload = ErrorSet },
5176 .type_inferred_error_set = .{
5177 .summary = .@"@typeInfo(@typeInfo(@TypeOf({.data%summary})).@\"fn\".return_type.?).error_union.error_set",
5178 .data = Index,
5179 },
5180 .simple_type = .{ .summary = .@"{.index%value#.}", .index = SimpleType },
5181 .type_tuple = .{
5182 .summary = .@"struct {...}",
5183 .payload = TypeTuple,
5184 .trailing = struct {
5185 field_types: []Index,
5186 field_values: []Index,
5187 },
5188 .config = .{
5189 .@"trailing.field_types.len" = .@"payload.fields_len",
5190 .@"trailing.field_values.len" = .@"payload.fields_len",
5191 },
5192 },
5193 .type_function = .{
5194 .summary = .@"fn (...) ... {.payload.return_type%summary}",
5195 .payload = TypeFunction,
5196 .trailing = struct {
5197 param_comptime_bits: ?[]u32,
5198 param_noalias_bits: ?[]u32,
5199 spirv_kernel_options: ?extern struct { x: u32, y: u32, z: u32 },
5200 spirv_mesh_options: ?extern struct { max_primitives: u32, max_vertices: u32, x: u32, y: u32, z: u32 },
5201 param_types: []Index,
5202 },
5203 .config = .{
5204 .@"trailing.param_comptime_bits.?" = .@"payload.flags.has_comptime_bits",
5205 .@"trailing.param_comptime_bits.?.len" = .@"(payload.params_len + 31) / 32",
5206 .@"trailing.param_noalias_bits.?" = .@"payload.flags.has_noalias_bits",
5207 .@"trailing.param_noalias_bits.?.len" = .@"(payload.params_len + 31) / 32",
5208 .@"trailing.spirv_kernel_options.?" = .@"payload.flags.cc.tag == .spirv_kernel or payload.flags.cc.tag == .spirv_task",
5209 .@"trailing.spirv_mesh_options.?" = .@"payload.flags.cc.tag == .spirv_mesh",
5210 .@"trailing.param_types.len" = .@"payload.params_len",
5211 },
5212 },
5213
5214 .type_struct = .{
5215 .summary = .@"{.payload.name%summary#\"}",
5216 .payload = TypeStruct,
5217 .trailing = struct {
5218 type_hash: ?u64,
5219 captures_len: ?u32,
5220 captures: ?[]CaptureValue,
5221 field_names: []NullTerminatedString,
5222 field_types: []Index,
5223 field_defaults: ?[]Index,
5224 field_aligns: ?[]Alignment,
5225 field_is_comptime_bits: ?[]u32,
5226 field_runtime_order: ?[]u32,
5227 field_offsets: []u32,
5228 },
5229 .config = .{
5230 .@"trailing.type_hash.?" = .@"payload.flags.any_captures == .reified",
5231 .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true",
5232 .@"trailing.captures.?" = .@"payload.flags.any_captures == .true",
5233 .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
5234 .@"trailing.field_names.len" = .@"payload.fields_len",
5235 .@"trailing.field_types.len" = .@"payload.fields_len",
5236 .@"trailing.field_defaults.?" = .@"payload.flags.any_field_defaults",
5237 .@"trailing.field_defaults.?.len" = .@"payload.fields_len",
5238 .@"trailing.field_aligns.?" = .@"payload.flags.any_field_aligns",
5239 .@"trailing.field_aligns.?.len" = .@"(payload.fields_len + 3) / 4",
5240 .@"trailing.field_is_comptime_bits.?" = .@"payload.flags.any_comptime_fields",
5241 .@"trailing.field_is_comptime_bits.?.len" = .@"(payload.fields_len + 31) / 32",
5242 .@"trailing.field_runtime_order.?" = .@"payload.flags.layout == .auto",
5243 .@"trailing.field_runtime_order.?.len" = .@"payload.fields_len",
5244 .@"trailing.field_offsets.len" = .@"payload.fields_len",
5245 },
5246 },
5247 .type_struct_packed_auto = struct_packed_encoding,
5248 .type_struct_packed_explicit = struct_packed_encoding,
5249 .type_struct_packed_auto_defaults = struct_packed_defaults_encoding,
5250 .type_struct_packed_explicit_defaults = struct_packed_defaults_encoding,
5251 .type_union = .{
5252 .summary = .@"{.payload.name%summary#\"}",
5253 .payload = TypeUnion,
5254 .trailing = struct {
5255 type_hash: ?u64,
5256 captures_len: ?u32,
5257 captures: ?[]CaptureValue,
5258 field_types: []Index,
5259 field_aligns: ?[]Alignment,
5260 },
5261 .config = .{
5262 .@"trailing.type_hash.?" = .@"payload.flags.any_captures == .reified",
5263 .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true",
5264 .@"trailing.captures.?" = .@"payload.flags.any_captures == .true",
5265 .@"trailing.captures.?.len" = .@"trailing.captures_len.?",
5266 .@"trailing.field_types.len" = .@"payload.fields_len",
5267 .@"trailing.field_aligns.?" = .@"payloads.flags.any_field_aligns",
5268 .@"trailing.field_aligns.?.len" = .@"(payload.fields_len + 3) / 4",
5269 },
5270 },
5271 .type_union_packed_auto = union_packed_encoding,
5272 .type_union_packed_explicit = union_packed_encoding,
5273 .type_enum_auto = .{
5274 .summary = .@"{.payload.name%summary#\"}",
5275 .payload = TypeEnum,
5276 .trailing = struct {
5277 owner_union: ?Index,
5278 zir_index: ?TrackedInst.Index,
5279 type_hash: ?u64,
5280 captures: ?[]CaptureValue,
5281 field_names: []NullTerminatedString,
5282 },
5283 .config = .{
5284 .@"trailing.owner_union.?" = .@"payload.bits.captures_len == .generated_union_tag",
5285 .@"trailing.zir_index.?" = .@"payload.bits.captures_len != .generated_union_tag",
5286 .@"trailing.type_hash.?" = .@"payload.bits.captures_len == .reified",
5287 .@"trailing.captures.?" = .@"payload.bits.captures_len != .reified and payload.bits.captures_len != .generated_union_tag",
5288 .@"trailing.captures.?.len" = .@"@intFromEnum(payload.bits.captures_len)",
5289 .@"trailing.field_names.len" = .@"payload.fields_len",
5290 },
5291 },
5292 .type_enum_explicit = enum_explicit_encoding,
5293 .type_enum_nonexhaustive = enum_explicit_encoding,
5294 .type_spirv = .{ .payload = Tag.TypeSpirv },
5295 .type_opaque = .{
5296 .summary = .@"{.payload.name%summary#\"}",
5297 .payload = TypeOpaque,
5298 .trailing = struct { captures: []CaptureValue },
5299 .config = .{ .@"trailing.captures.len" = .@"payload.captures_len" },
5300 },
5301
5302 .undef = .{ .summary = .@"@as({.data%summary}, undefined)", .data = Index },
5303 .simple_value = .{ .summary = .@"{.index%value#.}", .index = SimpleValue },
5304 .ptr_nav = .{
5305 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&{.payload.nav.fqn%summary#\"}) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5306 .payload = PtrNav,
5307 },
5308 .ptr_comptime_alloc = .{
5309 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&comptime_allocs[{.payload.index%summary}]) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5310 .payload = PtrComptimeAlloc,
5311 },
5312 .ptr_uav = .{
5313 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&{.payload.val%summary}) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5314 .payload = PtrUav,
5315 },
5316 .ptr_uav_aligned = .{
5317 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(@as({.payload.orig_ty%summary}, &{.payload.val%summary})) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5318 .payload = PtrUavAligned,
5319 },
5320 .ptr_comptime_field = .{
5321 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&{.payload.field_val%summary}) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5322 .payload = PtrComptimeField,
5323 },
5324 .ptr_int = .{
5325 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value}))",
5326 .payload = PtrInt,
5327 },
5328 .ptr_eu_payload = .{
5329 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&({.payload.base%summary} catch unreachable)) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5330 .payload = PtrBase,
5331 },
5332 .ptr_opt_payload = .{
5333 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&{.payload.base%summary}.?) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5334 .payload = PtrBase,
5335 },
5336 .ptr_elem = .{
5337 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&{.payload.base%summary}[{.payload.index%summary}]) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5338 .payload = PtrBaseIndex,
5339 },
5340 .ptr_field = .{
5341 .summary = .@"@as({.payload.ty%summary}, @ptrFromInt(@intFromPtr(&{.payload.base%summary}[{.payload.index%summary}]) + ({.payload.byte_offset_a%value} << 32 | {.payload.byte_offset_b%value})))",
5342 .payload = PtrBaseIndex,
5343 },
5344 .ptr_slice = .{
5345 .summary = .@"{.payload.ptr%summary}[0..{.payload.len%summary}]",
5346 .payload = PtrSlice,
5347 },
5348 .opt_payload = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.val%summary})", .payload = TypeValue },
5349 .opt_null = .{ .summary = .@"@as({.data%summary}, null)", .data = Index },
5350 .int_u8 = .{ .summary = .@"@as(u8, {.data%value})", .data = u8 },
5351 .int_u16 = .{ .summary = .@"@as(u16, {.data%value})", .data = u16 },
5352 .int_u32 = .{ .summary = .@"@as(u32, {.data%value})", .data = u32 },
5353 .int_i32 = .{ .summary = .@"@as(i32, {.data%value})", .data = i32 },
5354 .int_usize = .{ .summary = .@"@as(usize, {.data%value})", .data = u32 },
5355 .int_comptime_int_u32 = .{ .summary = .@"{.data%value}", .data = u32 },
5356 .int_comptime_int_i32 = .{ .summary = .@"{.data%value}", .data = i32 },
5357 .int_small = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.value%value})", .payload = IntSmall },
5358 .int_positive = .{},
5359 .int_negative = .{},
5360 .error_set_error = .{ .summary = .@"@as({.payload.ty%summary}, error.@{.payload.name%summary})", .payload = Error },
5361 .error_union_error = .{ .summary = .@"@as({.payload.ty%summary}, error.@{.payload.name%summary})", .payload = Error },
5362 .error_union_payload = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.val%summary})", .payload = TypeValue },
5363 .enum_literal = .{ .summary = .@".@{.data%summary}", .data = NullTerminatedString },
5364 .enum_tag = .{ .summary = .@"@as({.payload.ty%summary}, @enumFromInt({.payload.int%summary}))", .payload = EnumTag },
5365 .float_f16 = .{ .summary = .@"@as(f16, {.data%value})", .data = f16 },
5366 .float_f32 = .{ .summary = .@"@as(f32, {.data%value})", .data = f32 },
5367 .float_f64 = .{ .summary = .@"@as(f64, {.payload%value})", .payload = f64 },
5368 .float_f80 = .{ .summary = .@"@as(f80, {.payload%value})", .payload = f80 },
5369 .float_f128 = .{ .summary = .@"@as(f128, {.payload%value})", .payload = f128 },
5370 .float_c_longdouble_f80 = .{ .summary = .@"@as(c_longdouble, {.payload%value})", .payload = f80 },
5371 .float_c_longdouble_f128 = .{ .summary = .@"@as(c_longdouble, {.payload%value})", .payload = f128 },
5372 .float_comptime_float = .{ .summary = .@"{.payload%value}", .payload = f128 },
5373 .@"extern" = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Extern },
5374 .func_decl = .{
5375 .summary = .@"{.payload.owner_nav.fqn%summary#\"}",
5376 .payload = FuncDecl,
5377 .trailing = struct { inferred_error_set: ?Index },
5378 .config = .{ .@"trailing.inferred_error_set.?" = .@"payload.analysis.inferred_error_set" },
5379 },
5380 .func_instance = .{
5381 .summary = .@"{.payload.owner_nav.fqn%summary#\"}",
5382 .payload = FuncInstance,
5383 .trailing = struct {
5384 inferred_error_set: ?Index,
5385 param_values: []Index,
5386 },
5387 .config = .{
5388 .@"trailing.inferred_error_set.?" = .@"payload.analysis.inferred_error_set",
5389 .@"trailing.param_values.len" = .@"payload.ty.payload.params_len",
5390 },
5391 },
5392 .func_coerced = .{
5393 .summary = .@"@as(*const {.payload.ty%summary}, @ptrCast(&{.payload.func%summary})).*",
5394 .payload = FuncCoerced,
5395 },
5396 .only_possible_value = .{ .summary = .@"@as({.data%summary}, undefined)", .data = Index },
5397 .union_value = .{ .summary = .@"@as({.payload.ty%summary}, {})", .payload = Union },
5398 .bytes = .{ .summary = .@"@as({.payload.ty%summary}, {.payload.bytes%summary}.*)", .payload = Bytes },
5399 .aggregate = .{
5400 .summary = .@"@as({.payload.ty%summary}, .{...})",
5401 .payload = Aggregate,
5402 .trailing = struct { elements: []Index },
5403 .config = .{ .@"trailing.elements.len" = .@"payload.ty.payload.fields_len" },
5404 },
5405 .repeated = .{ .summary = .@"@as({.payload.ty%summary}, @splat({.payload.elem_val%summary}))", .payload = Repeated },
5406 .bitpack = .{ .summary = .@"@as({.payload.ty%summary}, {})", .payload = Key.Bitpack },
5407
5408 .memoized_call = .{
5409 .summary = .@"@memoize({.payload.func%summary})",
5410 .payload = MemoizedCall,
5411 .trailing = struct { arg_values: []Index },
5412 .config = .{ .@"trailing.arg_values.len" = .@"payload.args_len" },
5413 },
5414 };
5415 fn Payload(comptime tag: Tag) type {
5416 return @field(encodings, @tagName(tag)).payload;
5417 }
5418
5419 pub const Extern = struct {
5420 // name, is_const, alignment, addrspace come from `owner_nav`.
5421 ty: Index,
5422 lib_name: OptionalNullTerminatedString,
5423 flags: Flags,
5424 owner_nav: Nav.Index,
5425 zir_index: TrackedInst.Index,
5426 location_or_descriptor_set: u32,
5427 descriptor_binding: u32,
5428
5429 pub const Flags = packed struct(u32) {
5430 linkage: std.lang.GlobalLinkage,
5431 visibility: std.lang.SymbolVisibility,
5432 is_dll_import: bool,
5433 relocation: std.lang.ExternOptions.Relocation,
5434 source: Source,
5435 decoration_type: DecorationType,
5436 _: u23 = 0,
5437
5438 pub const Source = enum(u1) { builtin, syntax };
5439 pub const DecorationType = enum(u2) { none, location, descriptor, flat };
5440 };
5441
5442 pub fn decoration(self: Extern) ?std.lang.ExternOptions.Decoration {
5443 return switch (self.flags.decoration_type) {
5444 .none => null,
5445 .location => std.lang.ExternOptions.Decoration{ .location = self.location_or_descriptor_set },
5446 .descriptor => std.lang.ExternOptions.Decoration{ .descriptor = .{ .set = self.location_or_descriptor_set, .binding = self.descriptor_binding } },
5447 .flat => std.lang.ExternOptions.Decoration{ .flat = self.location_or_descriptor_set },
5448 };
5449 }
5450 };
5451
5452 /// Trailing:
5453 /// 0. element: Index for each len
5454 /// len is determined by the aggregate type.
5455 pub const Aggregate = struct {
5456 /// The type of the aggregate.
5457 ty: Index,
5458 };
5459
5460 /// Trailing:
5461 /// 0. If `analysis.inferred_error_set` is `true`, `Index` of an `error_set` which
5462 /// is a regular error set corresponding to the finished inferred error set.
5463 /// A `none` value marks that the inferred error set is not resolved yet.
5464 pub const FuncDecl = struct {
5465 analysis: FuncAnalysis,
5466 owner_nav: Nav.Index,
5467 ty: Index,
5468 zir_body_inst: TrackedInst.Index,
5469 lbrace_line: u32,
5470 rbrace_line: u32,
5471 lbrace_column: u32,
5472 rbrace_column: u32,
5473 };
5474
5475 /// Trailing:
5476 /// 0. If `analysis.inferred_error_set` is `true`, `Index` of an `error_set` which
5477 /// is a regular error set corresponding to the finished inferred error set.
5478 /// A `none` value marks that the inferred error set is not resolved yet.
5479 /// 1. For each parameter of generic_owner: `Index` if comptime, otherwise `none`
5480 pub const FuncInstance = struct {
5481 analysis: FuncAnalysis,
5482 // Needed by the linker for codegen. Not part of hashing or equality.
5483 owner_nav: Nav.Index,
5484 ty: Index,
5485 branch_quota: u32,
5486 /// Points to a `FuncDecl`.
5487 generic_owner: Index,
5488 };
5489
5490 pub const FuncCoerced = struct {
5491 ty: Index,
5492 func: Index,
5493 };
5494
5495 /// Trailing:
5496 /// 0. name: NullTerminatedString for each names_len
5497 pub const ErrorSet = struct {
5498 names_len: u32,
5499 /// Maps error names to declaration index.
5500 names_map: MapIndex,
5501 };
5502
5503 /// Trailing:
5504 /// 0. comptime_bits: u32, // if has_comptime_bits
5505 /// 1. noalias_bits: u32, // if has_noalias_bits
5506 /// 2. param_type: Index for each params_len
5507 pub const TypeFunction = struct {
5508 params_len: u32,
5509 return_type: Index,
5510 flags: Flags,
5511
5512 pub const Flags = packed struct(u32) {
5513 cc: PackedCallingConvention,
5514 is_var_args: bool,
5515 has_comptime_bits: bool,
5516 has_noalias_bits: bool,
5517 is_noinline: bool,
5518 _: u10 = 0,
5519 };
5520 };
5521
5522 /// At first I thought of storing the denormalized data externally, such as...
5523 ///
5524 /// * runtime field order
5525 /// * calculated field offsets
5526 /// * size and alignment of the struct
5527 ///
5528 /// ...since these can be computed based on the other data here. However,
5529 /// this data does need to be memoized, and therefore stored in memory
5530 /// while the compiler is running, in order to avoid O(N^2) logic in many
5531 /// places. Since the data can be stored compactly in the InternPool
5532 /// representation, it is better for memory usage to store denormalized data
5533 /// here, and potentially also better for performance as well. It's also simpler
5534 /// than coming up with some other scheme for the data.
5535 ///
5536 /// Trailing:
5537 /// 0. type_hash: PackedU64 // if `any_captures == .reified`
5538 /// 1. captures_len: u32 // if `any_captures == .true`
5539 /// 2. capture: CaptureValue // for each `captures_len`
5540 /// 3. field_name: NullTerminatedString // for each `fields_len`
5541 /// 4. field_type: Index // for each `fields_len`
5542 /// 5. field_default: Index // if `any_field_defaults`; for each `fields_len`
5543 /// 6. field_align: Alignment // if `any_field_aligns`; for each `fields_len`
5544 /// 7. field_is_comptime_bits: u32 // if `any_comptime_fields`; minimum `u32` for `fields_len`; LSB is field 0
5545 /// 8. field_runtime_order: RuntimeOrder // if `layout == .auto`; for each `fields_len`
5546 /// 9. field_offset: u32 // for each `fields_len`
5547 pub const TypeStruct = struct {
5548 zir_index: TrackedInst.Index,
5549
5550 name: NullTerminatedString,
5551 fqn: NullTerminatedString,
5552 name_nav: Nav.Index.Optional,
5553 namespace: NamespaceIndex,
5554
5555 fields_len: u32,
5556 field_name_map: MapIndex,
5557
5558 /// Size in bytes of the whole struct. Always 0 until layout resolved.
5559 size: u32,
5560
5561 flags: Flags,
5562
5563 pub const Flags = packed struct(u32) {
5564 any_captures: enum(u2) { true, false, reified },
5565
5566 /// `packed` layout is represented separately by `TypeStructPacked`.
5567 layout: enum(u1) { auto, @"extern" },
5568
5569 any_comptime_fields: bool,
5570 any_field_defaults: bool,
5571 any_field_aligns: bool,
5572
5573 class: TypeClass,
5574 /// Alignment of the whole struct. Always `.none` until layout resolved.
5575 alignment: Alignment,
5576
5577 want_layout: bool,
5578
5579 _: u16 = 0,
5580 };
5581 };
5582
5583 /// Trailing:
5584 /// 0. type_hash: PackedU64 // if `captures_len == .reified`
5585 /// 1. capture: CaptureValue // if `captures_len != .reified`; for each `captures_len`
5586 /// 2. field_name: NullTerminatedString // for each `fields_len`
5587 /// 3. field_type: Index // for each `fields_len`
5588 /// 4. field_default: Index // if item tag implies field defaults; for each `fields_len`
5589 pub const TypeStructPacked = struct {
5590 zir_index: TrackedInst.Index,
5591 bits: Bits,
5592
5593 name: NullTerminatedString,
5594 fqn: NullTerminatedString,
5595 name_nav: Nav.Index.Optional,
5596 namespace: NamespaceIndex,
5597
5598 /// The corresponding `BackingTypeMode` depends on the item's `Tag`.
5599 backing_int_type: Index,
5600
5601 fields_len: u32,
5602 field_name_map: MapIndex,
5603
5604 const Bits = packed struct(u32) {
5605 captures_len: enum(u31) {
5606 reified = std.math.maxInt(u31),
5607 _,
5608 },
5609 want_layout: bool,
5610 };
5611 };
5612
5613 /// For declared unions, field names are intentionally omitted because they are available in
5614 /// `enum_tag_type`. However, reified unions do store field names, because they are needed by
5615 /// type resolution to create or validate the enum tag type (type resolution for declared unions
5616 /// instead fetches field names from ZIR).
5617 ///
5618 /// Trailing:
5619 /// 0. type_hash: PackedU64 // if `any_captures == .reified`
5620 /// 1. captures_len: u32 // if `any_captures == .true`
5621 /// 2. capture: CaptureValue // if `any_captures == .true`; for each `captures_len`
5622 /// 3. reified_field_name: NullTerminatedString // if `any_captures == .reified`; for each `fields_len`
5623 /// 4. field_type: Index // for each `fields_len`
5624 /// 5. field_align: Alignment // for each `fields_len` if `any_field_aligns`
5625 pub const TypeUnion = struct {
5626 zir_index: TrackedInst.Index,
5627
5628 name: NullTerminatedString,
5629 fqn: NullTerminatedString,
5630 name_nav: Nav.Index.Optional,
5631 namespace: NamespaceIndex,
5632 /// The enum that provides the list of field names and values.
5633 enum_tag_type: Index,
5634
5635 /// This could be provided through the tag type, but it is more convenient
5636 /// to store it directly. This is also necessary for `dumpStatsFallible` to
5637 /// work on unresolved types.
5638 fields_len: u32,
5639
5640 /// Always 0 until layout resolved.
5641 size: u32,
5642 /// Always 0 until layout resolved.
5643 padding: u32,
5644
5645 flags: Flags,
5646
5647 pub const Flags = packed struct(u32) {
5648 any_captures: enum(u2) { true, false, reified },
5649
5650 /// Whether `enum_tag_type` was explicitly specified with `union(E)` syntax.
5651 ///
5652 /// For `union(enum(E))` syntax, this is `false`, but the generated enum tag type is
5653 /// considered to have an explicitly specified integer tag type.
5654 enum_tag_mode: BackingTypeMode,
5655
5656 /// `packed` layout is represented separately by `TypeStructPacked`.
5657 layout: enum(u1) { auto, @"extern" },
5658
5659 any_field_aligns: bool,
5660 tag_usage: LoadedUnionType.TagUsage,
5661
5662 class: TypeClass,
5663 has_runtime_tag: bool,
5664
5665 /// Alignment of the whole union. Always `.none` until layout resolved.
5666 alignment: Alignment,
5667
5668 want_layout: bool,
5669
5670 _: u14 = 0,
5671 };
5672 };
5673
5674 /// For declared unions, field names are intentionally omitted because they are available in
5675 /// `enum_tag_type`. However, reified unions do store field names, because they are needed by
5676 /// type resolution to create or validate the enum tag type (type resolution for declared unions
5677 /// instead fetches field names from ZIR).
5678 ///
5679 /// Trailing:
5680 /// 0. type_hash: PackedU64 // if `captures_len == .reified`
5681 /// 1. capture: CaptureValue // if `captures_len != .reified`; for each `captures_len`
5682 /// 2. reified_field_name: NullTerminatedString // if `captures_len == .reified`; for each `fields_len`
5683 /// 3. field_type: Index // for each `fields_len`
5684 pub const TypeUnionPacked = struct {
5685 zir_index: TrackedInst.Index,
5686 bits: Bits,
5687
5688 name: NullTerminatedString,
5689 fqn: NullTerminatedString,
5690 name_nav: Nav.Index.Optional,
5691 namespace: NamespaceIndex,
5692
5693 /// The corresponding `BackingTypeMode` depends on the item's `Tag`.
5694 backing_int_type: Index,
5695 /// Although packed unions do not semantically have a tag type, the compiler still assigns
5696 /// them a "hypothetical" tag type.
5697 enum_tag_type: Index,
5698
5699 /// This could be provided through the tag type, but it is more convenient
5700 /// to store it directly. This is also necessary for `dumpStatsFallible` to
5701 /// work on unresolved types.
5702 fields_len: u32,
5703
5704 const Bits = packed struct(u32) {
5705 captures_len: enum(u31) {
5706 reified = std.math.maxInt(u31),
5707 _,
5708 },
5709 want_layout: bool,
5710 };
5711 };
5712
5713 /// Trailing:
5714 /// 0. owner_union: Index // if `captures_len == .generated_union_tag`
5715 /// 1. zir_index: TrackedInst.Index // if `captures_len != .generated_union_tag`
5716 /// 2. type_hash: PackedU64 // if `captures_len == .reified`
5717 /// 3. capture: CaptureValue // if `captures_len` is not a named tag; for each `captures_len`
5718 /// 4. field_value_map: MapIndex // if tag is not `.type_enum_auto`
5719 /// 5. field_name: NullTerminatedString // for each `fields_len`
5720 /// 6. field_value: Index // if tag is not `.type_enum_auto`; for each `fields_len`
5721 pub const TypeEnum = struct {
5722 bits: Bits,
5723
5724 name: NullTerminatedString,
5725 fqn: NullTerminatedString,
5726 name_nav: Nav.Index.Optional,
5727 namespace: NamespaceIndex,
5728
5729 /// An integer type which is used for the numerical value of the enum. Whether this was
5730 /// user-provided or inferred by the compiler depends on the tag.
5731 int_tag_type: Index,
5732
5733 fields_len: u32,
5734 field_name_map: MapIndex,
5735
5736 const Bits = packed struct(u32) {
5737 captures_len: enum(u31) {
5738 reified = std.math.maxInt(u31),
5739 generated_union_tag = std.math.maxInt(u31) - 1,
5740 _,
5741 },
5742 want_layout: bool,
5743 };
5744 };
5745
5746 /// Trailing:
5747 /// 0. capture: CaptureValue // for each `captures_len`
5748 pub const TypeOpaque = struct {
5749 zir_index: TrackedInst.Index,
5750 captures_len: u32,
5751
5752 name: NullTerminatedString,
5753 fqn: NullTerminatedString,
5754 name_nav: Nav.Index.Optional,
5755 namespace: NamespaceIndex,
5756 };
5757};
5758
5759/// Differentiates between user-provided and compiler-generated backing types for packed and tagged types.
5760pub const BackingTypeMode = enum(u1) {
5761 /// The backing type was explicitly provided by the user. For instance:
5762 /// union(T)
5763 /// enum(T)
5764 /// packed struct(T)
5765 /// packed union(T)
5766 /// Type layout resolution will evaluate the user-provided expression and validate that type.
5767 explicit,
5768 /// No backing type was explicitly provided by the user. Type layout resolution will populate
5769 /// an inferred/generated type.
5770 auto,
5771};
5772
5773/// State that is mutable during semantic analysis. This data is not used for
5774/// equality or hashing, except for `inferred_error_set` which is considered
5775/// to be part of the type of the function.
5776pub const FuncAnalysis = packed struct(u32) {
5777 want_runtime_analysis: bool,
5778 branch_hint: std.lang.BranchHint,
5779 is_noinline: bool,
5780 has_error_trace: bool,
5781 /// True if this function has an inferred error set.
5782 inferred_error_set: bool,
5783 disable_instrumentation: bool,
5784 disable_intrinsics: bool,
5785
5786 _: u23 = 0,
5787};
5788
5789pub const Bytes = struct {
5790 /// The type of the aggregate
5791 ty: Index,
5792 /// Index into strings, of len ip.aggregateTypeLen(ty)
5793 bytes: String,
5794};
5795
5796pub const Repeated = struct {
5797 /// The type of the aggregate.
5798 ty: Index,
5799 /// The value of every element.
5800 elem_val: Index,
5801};
5802
5803/// Trailing:
5804/// 0. type: Index for each fields_len
5805/// 1. value: Index for each fields_len
5806pub const TypeTuple = struct {
5807 fields_len: u32,
5808};
5809
5810/// Having `SimpleType` and `SimpleValue` in separate enums makes it easier to
5811/// implement logic that only wants to deal with types because the logic can
5812/// ignore all simple values. Note that technically, types are values.
5813pub const SimpleType = enum(u32) {
5814 f16 = @backingInt(Index.f16_type),
5815 f32 = @backingInt(Index.f32_type),
5816 f64 = @backingInt(Index.f64_type),
5817 f80 = @backingInt(Index.f80_type),
5818 f128 = @backingInt(Index.f128_type),
5819 usize = @backingInt(Index.usize_type),
5820 isize = @backingInt(Index.isize_type),
5821 c_char = @backingInt(Index.c_char_type),
5822 c_short = @backingInt(Index.c_short_type),
5823 c_ushort = @backingInt(Index.c_ushort_type),
5824 c_int = @backingInt(Index.c_int_type),
5825 c_uint = @backingInt(Index.c_uint_type),
5826 c_long = @backingInt(Index.c_long_type),
5827 c_ulong = @backingInt(Index.c_ulong_type),
5828 c_longlong = @backingInt(Index.c_longlong_type),
5829 c_ulonglong = @backingInt(Index.c_ulonglong_type),
5830 c_longdouble = @backingInt(Index.c_longdouble_type),
5831 anyopaque = @backingInt(Index.anyopaque_type),
5832 bool = @backingInt(Index.bool_type),
5833 void = @backingInt(Index.void_type),
5834 type = @backingInt(Index.type_type),
5835 anyerror = @backingInt(Index.anyerror_type),
5836 comptime_int = @backingInt(Index.comptime_int_type),
5837 comptime_float = @backingInt(Index.comptime_float_type),
5838 noreturn = @backingInt(Index.noreturn_type),
5839 null = @backingInt(Index.null_type),
5840 undefined = @backingInt(Index.undefined_type),
5841 enum_literal = @backingInt(Index.enum_literal_type),
5842
5843 adhoc_inferred_error_set = @backingInt(Index.adhoc_inferred_error_set_type),
5844 generic_poison = @backingInt(Index.generic_poison_type),
5845};
5846
5847pub const SimpleValue = enum(u32) {
5848 void = @backingInt(Index.void_value),
5849 /// This is untyped `null`.
5850 null = @backingInt(Index.null_value),
5851 true = @backingInt(Index.bool_true),
5852 false = @backingInt(Index.bool_false),
5853 @"unreachable" = @backingInt(Index.unreachable_value),
5854};
5855
5856/// Stored as a power-of-two, with one special value to indicate none.
5857pub const Alignment = enum(u6) {
5858 @"1" = 0,
5859 @"2" = 1,
5860 @"4" = 2,
5861 @"8" = 3,
5862 @"16" = 4,
5863 @"32" = 5,
5864 @"64" = 6,
5865 none = std.math.maxInt(u6),
5866 _,
5867
5868 pub fn toByteUnits(a: Alignment) ?u64 {
5869 return switch (a) {
5870 .none => null,
5871 else => @as(u64, 1) << @backingInt(a),
5872 };
5873 }
5874
5875 pub fn fromByteUnits(n: u64) Alignment {
5876 if (n == 0) return .none;
5877 assert(std.math.isPowerOfTwo(n));
5878 return @fromBackingInt(@intCast(@ctz(n)));
5879 }
5880
5881 pub fn fromNonzeroByteUnits(n: u64) Alignment {
5882 assert(n != 0);
5883 return fromByteUnits(n);
5884 }
5885
5886 pub fn toLog2Units(a: Alignment) u6 {
5887 assert(a != .none);
5888 return @backingInt(a);
5889 }
5890
5891 /// This is just a glorified `@enumFromInt` but using it can help
5892 /// document the intended conversion.
5893 /// The parameter uses a u32 for convenience at the callsite.
5894 pub fn fromLog2Units(a: u32) Alignment {
5895 assert(a != @backingInt(Alignment.none));
5896 return @fromBackingInt(@intCast(a));
5897 }
5898
5899 pub fn order(lhs: Alignment, rhs: Alignment) std.math.Order {
5900 assert(lhs != .none);
5901 assert(rhs != .none);
5902 return std.math.order(@backingInt(lhs), @backingInt(rhs));
5903 }
5904
5905 /// Relaxed comparison. We have this as default because a lot of callsites
5906 /// were upgraded from directly using comparison operators on byte units,
5907 /// with the `none` value represented by zero.
5908 /// Prefer `compareStrict` if possible.
5909 pub fn compare(lhs: Alignment, op: std.math.CompareOperator, rhs: Alignment) bool {
5910 return std.math.compare(lhs.toRelaxedCompareUnits(), op, rhs.toRelaxedCompareUnits());
5911 }
5912
5913 pub fn compareStrict(lhs: Alignment, op: std.math.CompareOperator, rhs: Alignment) bool {
5914 assert(lhs != .none);
5915 assert(rhs != .none);
5916 return std.math.compare(@backingInt(lhs), op, @backingInt(rhs));
5917 }
5918
5919 /// Treats `none` as zero.
5920 /// This matches previous behavior of using `@max` directly on byte units.
5921 /// Prefer `maxStrict` if possible.
5922 pub fn max(lhs: Alignment, rhs: Alignment) Alignment {
5923 if (lhs == .none) return rhs;
5924 if (rhs == .none) return lhs;
5925 return maxStrict(lhs, rhs);
5926 }
5927
5928 pub fn maxStrict(lhs: Alignment, rhs: Alignment) Alignment {
5929 assert(lhs != .none);
5930 assert(rhs != .none);
5931 return @fromBackingInt(@intCast(@max(@backingInt(lhs), @backingInt(rhs))));
5932 }
5933
5934 /// Treats `none` as zero.
5935 /// This matches previous behavior of using `@min` directly on byte units.
5936 /// Prefer `minStrict` if possible.
5937 pub fn min(lhs: Alignment, rhs: Alignment) Alignment {
5938 if (lhs == .none) return lhs;
5939 if (rhs == .none) return rhs;
5940 return minStrict(lhs, rhs);
5941 }
5942
5943 pub fn minStrict(lhs: Alignment, rhs: Alignment) Alignment {
5944 assert(lhs != .none);
5945 assert(rhs != .none);
5946 return @fromBackingInt(@intCast(@min(@backingInt(lhs), @backingInt(rhs))));
5947 }
5948
5949 /// Given a base address known to be aligned to `a`,
5950 /// computes the known alignment of base address plus `off`.
5951 pub fn offset(a: Alignment, off: u64) Alignment {
5952 return .fromLog2Units(@min(a.toLog2Units(), @ctz(off)));
5953 }
5954
5955 /// Align an address forwards to this alignment.
5956 pub fn forward(a: Alignment, addr: u64) u64 {
5957 assert(a != .none);
5958 const x = (@as(u64, 1) << @backingInt(a)) - 1;
5959 return (addr + x) & ~x;
5960 }
5961
5962 /// Align an address backwards to this alignment.
5963 pub fn backward(a: Alignment, addr: u64) u64 {
5964 assert(a != .none);
5965 const x = (@as(u64, 1) << @backingInt(a)) - 1;
5966 return addr & ~x;
5967 }
5968
5969 /// Check if an address is aligned to this amount.
5970 pub fn check(a: Alignment, addr: u64) bool {
5971 assert(a != .none);
5972 return @ctz(addr) >= @backingInt(a);
5973 }
5974
5975 /// An array of `Alignment` objects existing within the `extra` array.
5976 /// This type exists to provide a struct with lifetime that is
5977 /// not invalidated when items are added to the `InternPool`.
5978 pub const Slice = struct {
5979 tid: Zcu.PerThread.Id,
5980 start: u32,
5981 /// This is the number of alignment values, not the number of u32 elements.
5982 len: u32,
5983
5984 pub const empty: Slice = .{ .tid = .main, .start = 0, .len = 0 };
5985
5986 pub fn get(slice: Slice, ip: *const InternPool) []Alignment {
5987 const extra = ip.getLocalShared(slice.tid).extra.acquire();
5988 const bytes: []u8 = @ptrCast(extra.view().items(.@"0")[slice.start..]);
5989 return @ptrCast(bytes[0..slice.len]);
5990 }
5991
5992 /// If `slice` is empty (`slice.len == 0`), returns `.none`.
5993 /// Otherwise, asserts that `index < slice.len`, and returns the value at `index`.
5994 pub fn getOrNone(slice: Slice, ip: *const InternPool, index: usize) Alignment {
5995 if (slice.len == 0) return .none;
5996 return slice.get(ip)[index];
5997 }
5998 };
5999
6000 pub fn toRelaxedCompareUnits(a: Alignment) u8 {
6001 const n: u8 = @backingInt(a);
6002 assert(n <= @backingInt(Alignment.none));
6003 if (n == @backingInt(Alignment.none)) return 0;
6004 return n + 1;
6005 }
6006
6007 pub fn toLlvm(a: Alignment) std.zig.llvm.Builder.Alignment {
6008 return @fromBackingInt(@intCast(@backingInt(a)));
6009 }
6010};
6011
6012/// Used for non-sentineled arrays that have length fitting in u32, as well as
6013/// vectors.
6014pub const Vector = struct {
6015 len: u32,
6016 child: Index,
6017};
6018
6019pub const Array = struct {
6020 len0: u32,
6021 len1: u32,
6022 child: Index,
6023 sentinel: Index,
6024
6025 pub const Length = PackedU64;
6026
6027 pub fn getLength(a: Array) u64 {
6028 return (PackedU64{
6029 .a = a.len0,
6030 .b = a.len1,
6031 }).get();
6032 }
6033};
6034
6035pub const PackedU64 = packed struct(u64) {
6036 a: u32,
6037 b: u32,
6038
6039 pub fn get(x: PackedU64) u64 {
6040 return @bitCast(x);
6041 }
6042
6043 pub fn init(x: u64) PackedU64 {
6044 return @bitCast(x);
6045 }
6046};
6047
6048pub const PtrNav = struct {
6049 ty: Index,
6050 nav: Nav.Index,
6051 byte_offset_a: u32,
6052 byte_offset_b: u32,
6053 fn init(ty: Index, nav: Nav.Index, byte_offset: u64) @This() {
6054 return .{
6055 .ty = ty,
6056 .nav = nav,
6057 .byte_offset_a = @intCast(byte_offset >> 32),
6058 .byte_offset_b = @truncate(byte_offset),
6059 };
6060 }
6061 fn byteOffset(data: @This()) u64 {
6062 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6063 }
6064};
6065
6066pub const PtrUav = struct {
6067 ty: Index,
6068 val: Index,
6069 byte_offset_a: u32,
6070 byte_offset_b: u32,
6071 fn init(ty: Index, val: Index, byte_offset: u64) @This() {
6072 return .{
6073 .ty = ty,
6074 .val = val,
6075 .byte_offset_a = @intCast(byte_offset >> 32),
6076 .byte_offset_b = @truncate(byte_offset),
6077 };
6078 }
6079 fn byteOffset(data: @This()) u64 {
6080 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6081 }
6082};
6083
6084pub const PtrUavAligned = struct {
6085 ty: Index,
6086 val: Index,
6087 /// Must be nonequal to `ty`. Only the alignment from this value is important.
6088 orig_ty: Index,
6089 byte_offset_a: u32,
6090 byte_offset_b: u32,
6091 fn init(ty: Index, val: Index, orig_ty: Index, byte_offset: u64) @This() {
6092 return .{
6093 .ty = ty,
6094 .val = val,
6095 .orig_ty = orig_ty,
6096 .byte_offset_a = @intCast(byte_offset >> 32),
6097 .byte_offset_b = @truncate(byte_offset),
6098 };
6099 }
6100 fn byteOffset(data: @This()) u64 {
6101 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6102 }
6103};
6104
6105pub const PtrComptimeAlloc = struct {
6106 ty: Index,
6107 index: ComptimeAllocIndex,
6108 byte_offset_a: u32,
6109 byte_offset_b: u32,
6110 fn init(ty: Index, index: ComptimeAllocIndex, byte_offset: u64) @This() {
6111 return .{
6112 .ty = ty,
6113 .index = index,
6114 .byte_offset_a = @intCast(byte_offset >> 32),
6115 .byte_offset_b = @truncate(byte_offset),
6116 };
6117 }
6118 fn byteOffset(data: @This()) u64 {
6119 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6120 }
6121};
6122
6123pub const PtrComptimeField = struct {
6124 ty: Index,
6125 field_val: Index,
6126 byte_offset_a: u32,
6127 byte_offset_b: u32,
6128 fn init(ty: Index, field_val: Index, byte_offset: u64) @This() {
6129 return .{
6130 .ty = ty,
6131 .field_val = field_val,
6132 .byte_offset_a = @intCast(byte_offset >> 32),
6133 .byte_offset_b = @truncate(byte_offset),
6134 };
6135 }
6136 fn byteOffset(data: @This()) u64 {
6137 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6138 }
6139};
6140
6141pub const PtrBase = struct {
6142 ty: Index,
6143 base: Index,
6144 byte_offset_a: u32,
6145 byte_offset_b: u32,
6146 fn init(ty: Index, base: Index, byte_offset: u64) @This() {
6147 return .{
6148 .ty = ty,
6149 .base = base,
6150 .byte_offset_a = @intCast(byte_offset >> 32),
6151 .byte_offset_b = @truncate(byte_offset),
6152 };
6153 }
6154 fn byteOffset(data: @This()) u64 {
6155 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6156 }
6157};
6158
6159pub const PtrBaseIndex = struct {
6160 ty: Index,
6161 base: Index,
6162 index: Index,
6163 byte_offset_a: u32,
6164 byte_offset_b: u32,
6165 fn init(ty: Index, base: Index, index: Index, byte_offset: u64) @This() {
6166 return .{
6167 .ty = ty,
6168 .base = base,
6169 .index = index,
6170 .byte_offset_a = @intCast(byte_offset >> 32),
6171 .byte_offset_b = @truncate(byte_offset),
6172 };
6173 }
6174 fn byteOffset(data: @This()) u64 {
6175 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6176 }
6177};
6178
6179pub const PtrInt = struct {
6180 ty: Index,
6181 byte_offset_a: u32,
6182 byte_offset_b: u32,
6183 fn init(ty: Index, byte_offset: u64) @This() {
6184 return .{
6185 .ty = ty,
6186 .byte_offset_a = @intCast(byte_offset >> 32),
6187 .byte_offset_b = @truncate(byte_offset),
6188 };
6189 }
6190 fn byteOffset(data: @This()) u64 {
6191 return @as(u64, data.byte_offset_a) << 32 | data.byte_offset_b;
6192 }
6193};
6194
6195pub const PtrSlice = struct {
6196 /// The slice type.
6197 ty: Index,
6198 /// A many pointer value.
6199 ptr: Index,
6200 /// A usize value.
6201 len: Index,
6202};
6203
6204/// Trailing: Limb for every limbs_len
6205pub const Int = packed struct {
6206 ty: Index,
6207 limbs_len: u32,
6208
6209 const limbs_items_len = @divExact(@sizeOf(Int), @sizeOf(Limb));
6210};
6211
6212pub const IntSmall = struct {
6213 ty: Index,
6214 value: u32,
6215};
6216
6217/// A f64 value, broken up into 2 u32 parts.
6218pub const Float64 = struct {
6219 piece0: u32,
6220 piece1: u32,
6221
6222 pub fn get(self: Float64) f64 {
6223 const int_bits = @as(u64, self.piece0) | (@as(u64, self.piece1) << 32);
6224 return @bitCast(int_bits);
6225 }
6226
6227 fn pack(val: f64) Float64 {
6228 const bits: u64 = @bitCast(val);
6229 return .{
6230 .piece0 = @truncate(bits),
6231 .piece1 = @truncate(bits >> 32),
6232 };
6233 }
6234};
6235
6236/// A f80 value, broken up into 2 u32 parts and a u16 part zero-padded to a u32.
6237pub const Float80 = struct {
6238 piece0: u32,
6239 piece1: u32,
6240 piece2: u32, // u16 part, top bits
6241
6242 pub fn get(self: Float80) f80 {
6243 const int_bits = @as(u80, self.piece0) |
6244 (@as(u80, self.piece1) << 32) |
6245 (@as(u80, self.piece2) << 64);
6246 return @bitCast(int_bits);
6247 }
6248
6249 fn pack(val: f80) Float80 {
6250 const bits: u80 = @bitCast(val);
6251 return .{
6252 .piece0 = @truncate(bits),
6253 .piece1 = @truncate(bits >> 32),
6254 .piece2 = @truncate(bits >> 64),
6255 };
6256 }
6257};
6258
6259/// A f128 value, broken up into 4 u32 parts.
6260pub const Float128 = struct {
6261 piece0: u32,
6262 piece1: u32,
6263 piece2: u32,
6264 piece3: u32,
6265
6266 pub fn get(self: Float128) f128 {
6267 const int_bits = @as(u128, self.piece0) |
6268 (@as(u128, self.piece1) << 32) |
6269 (@as(u128, self.piece2) << 64) |
6270 (@as(u128, self.piece3) << 96);
6271 return @bitCast(int_bits);
6272 }
6273
6274 fn pack(val: f128) Float128 {
6275 const bits: u128 = @bitCast(val);
6276 return .{
6277 .piece0 = @truncate(bits),
6278 .piece1 = @truncate(bits >> 32),
6279 .piece2 = @truncate(bits >> 64),
6280 .piece3 = @truncate(bits >> 96),
6281 };
6282 }
6283};
6284
6285/// Trailing:
6286/// 0. arg value: Index for each args_len
6287pub const MemoizedCall = struct {
6288 func: Index,
6289 args_len: u32,
6290 result: Index,
6291 branch_count: u32,
6292 branch_quota: u32,
6293};
6294
6295pub fn init(ip: *InternPool, gpa: Allocator, io: Io, available_threads: usize) !void {
6296 errdefer ip.deinit(gpa, io);
6297 assert(ip.locals.len == 0 and ip.shards.len == 0);
6298 assert(available_threads > 0 and available_threads <= std.math.maxInt(u8));
6299
6300 const used_threads = if (single_threaded) 1 else @max(available_threads, 2);
6301 ip.locals = try gpa.alloc(Local, used_threads);
6302 @memset(ip.locals, .{
6303 .shared = .{
6304 .items = .empty,
6305 .extra = .empty,
6306 .limbs = .empty,
6307 .strings = .empty,
6308 .string_bytes = .empty,
6309 .tracked_insts = .empty,
6310 .files = .empty,
6311 .maps = .empty,
6312 .navs = .empty,
6313 .comptime_units = .empty,
6314
6315 .namespaces = .empty,
6316 },
6317 .mutate = .{
6318 .arena = .{},
6319
6320 .items = .empty,
6321 .extra = .empty,
6322 .limbs = .empty,
6323 .strings = .empty,
6324 .string_bytes = .empty,
6325 .tracked_insts = .empty,
6326 .files = .empty,
6327 .maps = .empty,
6328 .navs = .empty,
6329 .comptime_units = .empty,
6330
6331 .namespaces = .empty,
6332 },
6333 });
6334 for (ip.locals) |*local| try local.getMutableStrings(gpa, io).append(.{0});
6335
6336 ip.tid_width = @intCast(std.math.log2_int_ceil(usize, used_threads));
6337 ip.tid_shift_30 = if (single_threaded) 0 else 30 - ip.tid_width;
6338 ip.tid_shift_31 = if (single_threaded) 0 else 31 - ip.tid_width;
6339 ip.tid_shift_32 = if (single_threaded) 0 else ip.tid_shift_31 +| 1;
6340 ip.shards = try gpa.alloc(Shard, @as(usize, 1) << ip.tid_width);
6341 @memset(ip.shards, .{
6342 .shared = .{
6343 .map = .empty,
6344 .string_map = .empty,
6345 .tracked_inst_map = .empty,
6346 },
6347 .mutate = .{
6348 .map = .empty,
6349 .string_map = .empty,
6350 .tracked_inst_map = .empty,
6351 },
6352 });
6353
6354 // Reserve string index 0 for an empty string.
6355 assert((try ip.getOrPutString(gpa, io, .main, "", .no_embedded_nulls)) == .empty);
6356
6357 // This inserts all the statically-known values into the intern pool in the
6358 // order expected.
6359 for (&static_keys, 0..) |key, key_index| switch (@as(Index, @fromBackingInt(@intCast(key_index)))) {
6360 .empty_tuple_type => assert(try ip.getTupleType(gpa, io, .main, .{
6361 .types = &.{},
6362 .values = &.{},
6363 }) == .empty_tuple_type),
6364 else => |expected_index| assert(try ip.get(gpa, io, .main, key) == expected_index),
6365 };
6366
6367 if (std.debug.runtime_safety) {
6368 // Sanity check.
6369 assert(ip.indexToKey(.bool_true).simple_value == .true);
6370 assert(ip.indexToKey(.bool_false).simple_value == .false);
6371 }
6372}
6373
6374pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void {
6375 std.debug.assert(debug_state.intern_pool == null);
6376
6377 ip.src_hash_deps.deinit(gpa);
6378 ip.nav_val_deps.deinit(gpa);
6379 ip.nav_ty_deps.deinit(gpa);
6380 ip.func_ies_deps.deinit(gpa);
6381 ip.type_layout_deps.deinit(gpa);
6382 ip.struct_defaults_deps.deinit(gpa);
6383 ip.source_file_deps.deinit(gpa);
6384 ip.embed_file_deps.deinit(gpa);
6385 ip.namespace_deps.deinit(gpa);
6386 ip.namespace_name_deps.deinit(gpa);
6387
6388 ip.first_dependency.deinit(gpa);
6389
6390 ip.dep_entries.deinit(gpa);
6391 ip.free_dep_entries.deinit(gpa);
6392
6393 gpa.free(ip.shards);
6394 for (ip.locals) |*local| {
6395 const buckets_len = local.mutate.namespaces.buckets_list.len;
6396 if (buckets_len > 0) for (
6397 local.shared.namespaces.view().items(.@"0")[0..buckets_len],
6398 0..,
6399 ) |namespace_bucket, buckets_index| {
6400 for (namespace_bucket[0..if (buckets_index < buckets_len - 1)
6401 namespace_bucket.len
6402 else
6403 local.mutate.namespaces.last_bucket_len]) |*namespace|
6404 {
6405 namespace.pub_decls.deinit(gpa);
6406 namespace.priv_decls.deinit(gpa);
6407 namespace.comptime_decls.deinit(gpa);
6408 namespace.test_decls.deinit(gpa);
6409 }
6410 };
6411 const maps = local.getMutableMaps(gpa, io);
6412 if (maps.mutate.len > 0) for (maps.view().items(.@"0")) |*map| map.deinit(gpa);
6413 local.mutate.arena.promote(gpa).deinit();
6414 }
6415 gpa.free(ip.locals);
6416
6417 ip.* = undefined;
6418}
6419
6420pub const Active = struct {
6421 prev_ip: if (debug_state.enable) ?*const InternPool else void,
6422 pub fn deactivate(active: Active) void {
6423 if (!debug_state.enable) return;
6424 debug_state.intern_pool = active.prev_ip;
6425 }
6426};
6427pub fn activate(ip: *const InternPool) Active {
6428 if (!debug_state.enable) return .{ .prev_ip = {} };
6429 _ = Index.Unwrapped.debug_state;
6430 _ = String.debug_state;
6431 _ = OptionalString.debug_state;
6432 _ = NullTerminatedString.debug_state;
6433 _ = OptionalNullTerminatedString.debug_state;
6434 _ = TrackedInst.Index.debug_state;
6435 _ = TrackedInst.Index.Optional.debug_state;
6436 _ = Nav.Index.debug_state;
6437 _ = Nav.Index.Optional.debug_state;
6438 defer debug_state.intern_pool = ip;
6439 return .{ .prev_ip = debug_state.intern_pool };
6440}
6441
6442/// For debugger access only.
6443const debug_state = struct {
6444 const enable = switch (builtin.zig_backend) {
6445 else => false,
6446 .stage2_x86_64 => !builtin.strip_debug_info and build_options.io_mode == .threaded,
6447 };
6448 threadlocal var intern_pool: ?*const InternPool = null;
6449};
6450
6451pub fn indexToKey(ip: *const InternPool, index: Index) Key {
6452 assert(index != .none);
6453 const unwrapped_index = index.unwrap(ip);
6454 const item = unwrapped_index.getItem(ip);
6455 const data = item.data;
6456 return switch (item.tag) {
6457 .removed => unreachable,
6458 .type_int_signed => .{
6459 .int_type = .{
6460 .signedness = .signed,
6461 .bits = @intCast(data),
6462 },
6463 },
6464 .type_int_unsigned => .{
6465 .int_type = .{
6466 .signedness = .unsigned,
6467 .bits = @intCast(data),
6468 },
6469 },
6470 .type_array_big => {
6471 const array_info = extraData(unwrapped_index.getExtra(ip), Array, data);
6472 return .{ .array_type = .{
6473 .len = array_info.getLength(),
6474 .child = array_info.child,
6475 .sentinel = array_info.sentinel,
6476 } };
6477 },
6478 .type_array_small => {
6479 const array_info = extraData(unwrapped_index.getExtra(ip), Vector, data);
6480 return .{ .array_type = .{
6481 .len = array_info.len,
6482 .child = array_info.child,
6483 .sentinel = .none,
6484 } };
6485 },
6486 .simple_type => .{ .simple_type = @fromBackingInt(@intCast(@backingInt(index))) },
6487 .simple_value => .{ .simple_value = @fromBackingInt(@intCast(@backingInt(index))) },
6488
6489 .type_vector => {
6490 const vector_info = extraData(unwrapped_index.getExtra(ip), Vector, data);
6491 return .{ .vector_type = .{
6492 .len = vector_info.len,
6493 .child = vector_info.child,
6494 } };
6495 },
6496
6497 .type_pointer => .{ .ptr_type = extraData(unwrapped_index.getExtra(ip), Tag.TypePointer, data) },
6498
6499 .type_slice => {
6500 const many_ptr_index: Index = @fromBackingInt(@intCast(data));
6501 const many_ptr_unwrapped = many_ptr_index.unwrap(ip);
6502 const many_ptr_item = many_ptr_unwrapped.getItem(ip);
6503 assert(many_ptr_item.tag == .type_pointer);
6504 var ptr_info = extraData(many_ptr_unwrapped.getExtra(ip), Tag.TypePointer, many_ptr_item.data);
6505 ptr_info.flags.size = .slice;
6506 return .{ .ptr_type = ptr_info };
6507 },
6508
6509 .type_optional => .{ .opt_type = @fromBackingInt(@intCast(data)) },
6510 .type_anyframe => .{ .anyframe_type = @fromBackingInt(@intCast(data)) },
6511
6512 .type_error_union => .{ .error_union_type = extraData(unwrapped_index.getExtra(ip), Key.ErrorUnionType, data) },
6513 .type_anyerror_union => .{ .error_union_type = .{
6514 .error_set_type = .anyerror_type,
6515 .payload_type = @fromBackingInt(@intCast(data)),
6516 } },
6517 .type_error_set => .{ .error_set_type = extraErrorSet(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
6518 .type_inferred_error_set => .{
6519 .inferred_error_set_type = @fromBackingInt(@intCast(data)),
6520 },
6521 .type_function => .{ .func_type = extraFuncType(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
6522 .type_tuple => .{ .tuple_type = extraTypeTuple(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
6523
6524 .type_struct => .{ .struct_type = ns: {
6525 const extra_list = unwrapped_index.getExtra(ip);
6526 const extra = extraDataTrail(extra_list, Tag.TypeStruct, data);
6527 break :ns switch (extra.data.flags.any_captures) {
6528 .reified => .{ .reified = .{
6529 .zir_index = extra.data.zir_index,
6530 .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
6531 } },
6532 .false => .{ .declared = .{
6533 .zir_index = extra.data.zir_index,
6534 .captures = .{ .owned = .empty },
6535 } },
6536 .true => .{ .declared = .{
6537 .zir_index = extra.data.zir_index,
6538 .captures = .{ .owned = .{
6539 .tid = unwrapped_index.tid,
6540 .start = extra.end + 1,
6541 .len = extra_list.view().items(.@"0")[extra.end],
6542 } },
6543 } },
6544 };
6545 } },
6546 .type_struct_packed_auto,
6547 .type_struct_packed_explicit,
6548 .type_struct_packed_auto_defaults,
6549 .type_struct_packed_explicit_defaults,
6550 => .{ .struct_type = ns: {
6551 const extra_list = unwrapped_index.getExtra(ip);
6552 const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
6553 break :ns switch (extra.data.bits.captures_len) {
6554 .reified => .{ .reified = .{
6555 .zir_index = extra.data.zir_index,
6556 .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
6557 } },
6558 _ => |len| .{ .declared = .{
6559 .zir_index = extra.data.zir_index,
6560 .captures = .{ .owned = .{
6561 .tid = unwrapped_index.tid,
6562 .start = extra.end,
6563 .len = @backingInt(len),
6564 } },
6565 } },
6566 };
6567 } },
6568 .type_union => .{ .union_type = ns: {
6569 const extra_list = unwrapped_index.getExtra(ip);
6570 const extra = extraDataTrail(extra_list, Tag.TypeUnion, data);
6571 break :ns switch (extra.data.flags.any_captures) {
6572 .reified => .{ .reified = .{
6573 .zir_index = extra.data.zir_index,
6574 .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
6575 } },
6576 .false => .{ .declared = .{
6577 .zir_index = extra.data.zir_index,
6578 .captures = .{ .owned = .empty },
6579 } },
6580 .true => .{ .declared = .{
6581 .zir_index = extra.data.zir_index,
6582 .captures = .{ .owned = .{
6583 .tid = unwrapped_index.tid,
6584 .start = extra.end + 1,
6585 .len = extra_list.view().items(.@"0")[extra.end],
6586 } },
6587 } },
6588 };
6589 } },
6590 .type_union_packed_auto, .type_union_packed_explicit => .{ .union_type = ns: {
6591 const extra_list = unwrapped_index.getExtra(ip);
6592 const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data);
6593 break :ns switch (extra.data.bits.captures_len) {
6594 .reified => .{ .reified = .{
6595 .zir_index = extra.data.zir_index,
6596 .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
6597 } },
6598 _ => |len| .{ .declared = .{
6599 .zir_index = extra.data.zir_index,
6600 .captures = .{ .owned = .{
6601 .tid = unwrapped_index.tid,
6602 .start = extra.end,
6603 .len = @backingInt(len),
6604 } },
6605 } },
6606 };
6607 } },
6608 .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: {
6609 const extra_list = unwrapped_index.getExtra(ip);
6610 const extra = extraDataTrail(extra_list, Tag.TypeEnum, data);
6611 break :ns switch (extra.data.bits.captures_len) {
6612 .reified => .{ .reified = .{
6613 .zir_index = @fromBackingInt(@intCast(extra_list.view().items(.@"0")[extra.end])),
6614 .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(),
6615 } },
6616 .generated_union_tag => .{ .generated_union_tag = owner_union: {
6617 break :owner_union @fromBackingInt(@intCast(extra_list.view().items(.@"0")[extra.end]));
6618 } },
6619 _ => |len| .{ .declared = .{
6620 .zir_index = @fromBackingInt(@intCast(extra_list.view().items(.@"0")[extra.end])),
6621 .captures = .{ .owned = .{
6622 .tid = unwrapped_index.tid,
6623 .start = extra.end + 1,
6624 .len = @backingInt(len),
6625 } },
6626 } },
6627 };
6628 } },
6629 .type_spirv => .{ .spirv_type = ns: {
6630 const extra = extraData(unwrapped_index.getExtra(ip), Tag.TypeSpirv, data);
6631 break :ns .{
6632 .ty = extra.ty,
6633 .flags = extra.flags,
6634 };
6635 } },
6636 .type_opaque => .{ .opaque_type = ns: {
6637 const extra = extraDataTrail(unwrapped_index.getExtra(ip), Tag.TypeOpaque, data);
6638 break :ns .{ .declared = .{
6639 .zir_index = extra.data.zir_index,
6640 .captures = .{ .owned = .{
6641 .tid = unwrapped_index.tid,
6642 .start = extra.end,
6643 .len = extra.data.captures_len,
6644 } },
6645 } };
6646 } },
6647
6648 .undef => .{ .undef = @fromBackingInt(@intCast(data)) },
6649 .opt_null => .{ .opt = .{
6650 .ty = @fromBackingInt(@intCast(data)),
6651 .val = .none,
6652 } },
6653 .opt_payload => {
6654 const extra = extraData(unwrapped_index.getExtra(ip), Tag.TypeValue, data);
6655 return .{ .opt = .{
6656 .ty = extra.ty,
6657 .val = extra.val,
6658 } };
6659 },
6660 .ptr_nav => {
6661 const info = extraData(unwrapped_index.getExtra(ip), PtrNav, data);
6662 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .nav = info.nav }, .byte_offset = info.byteOffset() } };
6663 },
6664 .ptr_comptime_alloc => {
6665 const info = extraData(unwrapped_index.getExtra(ip), PtrComptimeAlloc, data);
6666 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .comptime_alloc = info.index }, .byte_offset = info.byteOffset() } };
6667 },
6668 .ptr_uav => {
6669 const info = extraData(unwrapped_index.getExtra(ip), PtrUav, data);
6670 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .uav = .{
6671 .val = info.val,
6672 .orig_ty = info.ty,
6673 } }, .byte_offset = info.byteOffset() } };
6674 },
6675 .ptr_uav_aligned => {
6676 const info = extraData(unwrapped_index.getExtra(ip), PtrUavAligned, data);
6677 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .uav = .{
6678 .val = info.val,
6679 .orig_ty = info.orig_ty,
6680 } }, .byte_offset = info.byteOffset() } };
6681 },
6682 .ptr_comptime_field => {
6683 const info = extraData(unwrapped_index.getExtra(ip), PtrComptimeField, data);
6684 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .comptime_field = info.field_val }, .byte_offset = info.byteOffset() } };
6685 },
6686 .ptr_int => {
6687 const info = extraData(unwrapped_index.getExtra(ip), PtrInt, data);
6688 return .{ .ptr = .{
6689 .ty = info.ty,
6690 .base_addr = .int,
6691 .byte_offset = info.byteOffset(),
6692 } };
6693 },
6694 .ptr_eu_payload => {
6695 const info = extraData(unwrapped_index.getExtra(ip), PtrBase, data);
6696 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .eu_payload = info.base }, .byte_offset = info.byteOffset() } };
6697 },
6698 .ptr_opt_payload => {
6699 const info = extraData(unwrapped_index.getExtra(ip), PtrBase, data);
6700 return .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .opt_payload = info.base }, .byte_offset = info.byteOffset() } };
6701 },
6702 .ptr_elem => {
6703 // Avoid `indexToKey` recursion by asserting the tag encoding.
6704 const info = extraData(unwrapped_index.getExtra(ip), PtrBaseIndex, data);
6705 const index_item = info.index.unwrap(ip).getItem(ip);
6706 return switch (index_item.tag) {
6707 .int_usize => .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .arr_elem = .{
6708 .base = info.base,
6709 .index = index_item.data,
6710 } }, .byte_offset = info.byteOffset() } },
6711 .int_positive => @panic("TODO"), // implement along with behavior test coverage
6712 else => unreachable,
6713 };
6714 },
6715 .ptr_field => {
6716 // Avoid `indexToKey` recursion by asserting the tag encoding.
6717 const info = extraData(unwrapped_index.getExtra(ip), PtrBaseIndex, data);
6718 const index_item = info.index.unwrap(ip).getItem(ip);
6719 return switch (index_item.tag) {
6720 .int_usize => .{ .ptr = .{ .ty = info.ty, .base_addr = .{ .field = .{
6721 .base = info.base,
6722 .index = index_item.data,
6723 } }, .byte_offset = info.byteOffset() } },
6724 .int_positive => @panic("TODO"), // implement along with behavior test coverage
6725 else => unreachable,
6726 };
6727 },
6728 .ptr_slice => {
6729 const info = extraData(unwrapped_index.getExtra(ip), PtrSlice, data);
6730 return .{ .slice = .{
6731 .ty = info.ty,
6732 .ptr = info.ptr,
6733 .len = info.len,
6734 } };
6735 },
6736 .int_u8 => .{ .int = .{
6737 .ty = .u8_type,
6738 .storage = .{ .u64 = data },
6739 } },
6740 .int_u16 => .{ .int = .{
6741 .ty = .u16_type,
6742 .storage = .{ .u64 = data },
6743 } },
6744 .int_u32 => .{ .int = .{
6745 .ty = .u32_type,
6746 .storage = .{ .u64 = data },
6747 } },
6748 .int_i32 => .{ .int = .{
6749 .ty = .i32_type,
6750 .storage = .{ .i64 = @as(i32, @bitCast(data)) },
6751 } },
6752 .int_usize => .{ .int = .{
6753 .ty = .usize_type,
6754 .storage = .{ .u64 = data },
6755 } },
6756 .int_comptime_int_u32 => .{ .int = .{
6757 .ty = .comptime_int_type,
6758 .storage = .{ .u64 = data },
6759 } },
6760 .int_comptime_int_i32 => .{ .int = .{
6761 .ty = .comptime_int_type,
6762 .storage = .{ .i64 = @as(i32, @bitCast(data)) },
6763 } },
6764 .int_positive => ip.indexToKeyBigInt(unwrapped_index.tid, data, true),
6765 .int_negative => ip.indexToKeyBigInt(unwrapped_index.tid, data, false),
6766 .int_small => {
6767 const info = extraData(unwrapped_index.getExtra(ip), IntSmall, data);
6768 return .{ .int = .{
6769 .ty = info.ty,
6770 .storage = .{ .u64 = info.value },
6771 } };
6772 },
6773 .float_f16 => .{ .float = .{
6774 .ty = .f16_type,
6775 .storage = .{ .f16 = @bitCast(@as(u16, @intCast(data))) },
6776 } },
6777 .float_f32 => .{ .float = .{
6778 .ty = .f32_type,
6779 .storage = .{ .f32 = @bitCast(data) },
6780 } },
6781 .float_f64 => .{ .float = .{
6782 .ty = .f64_type,
6783 .storage = .{ .f64 = extraData(unwrapped_index.getExtra(ip), Float64, data).get() },
6784 } },
6785 .float_f80 => .{ .float = .{
6786 .ty = .f80_type,
6787 .storage = .{ .f80 = extraData(unwrapped_index.getExtra(ip), Float80, data).get() },
6788 } },
6789 .float_f128 => .{ .float = .{
6790 .ty = .f128_type,
6791 .storage = .{ .f128 = extraData(unwrapped_index.getExtra(ip), Float128, data).get() },
6792 } },
6793 .float_c_longdouble_f80 => .{ .float = .{
6794 .ty = .c_longdouble_type,
6795 .storage = .{ .f80 = extraData(unwrapped_index.getExtra(ip), Float80, data).get() },
6796 } },
6797 .float_c_longdouble_f128 => .{ .float = .{
6798 .ty = .c_longdouble_type,
6799 .storage = .{ .f128 = extraData(unwrapped_index.getExtra(ip), Float128, data).get() },
6800 } },
6801 .float_comptime_float => .{ .float = .{
6802 .ty = .comptime_float_type,
6803 .storage = .{ .f128 = extraData(unwrapped_index.getExtra(ip), Float128, data).get() },
6804 } },
6805 .@"extern" => {
6806 const extra = extraData(unwrapped_index.getExtra(ip), Tag.Extern, data);
6807 const nav = ip.getNav(extra.owner_nav);
6808 return .{ .@"extern" = .{
6809 .name = nav.name,
6810 .ty = extra.ty,
6811 .lib_name = extra.lib_name,
6812 .linkage = extra.flags.linkage,
6813 .visibility = extra.flags.visibility,
6814 .is_threadlocal = nav.resolved.?.@"threadlocal",
6815 .is_dll_import = extra.flags.is_dll_import,
6816 .relocation = extra.flags.relocation,
6817 .decoration = extra.decoration(),
6818 .is_const = nav.resolved.?.@"const",
6819 .alignment = nav.resolved.?.@"align",
6820 .@"addrspace" = nav.resolved.?.@"addrspace",
6821 .zir_index = extra.zir_index,
6822 .owner_nav = extra.owner_nav,
6823 .source = extra.flags.source,
6824 } };
6825 },
6826 .func_instance => .{ .func = ip.extraFuncInstance(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
6827 .func_decl => .{ .func = extraFuncDecl(unwrapped_index.tid, unwrapped_index.getExtra(ip), data) },
6828 .func_coerced => .{ .func = ip.extraFuncCoerced(unwrapped_index.getExtra(ip), data) },
6829 .only_possible_value => {
6830 const ty: Index = @fromBackingInt(@intCast(data));
6831 const ty_unwrapped = ty.unwrap(ip);
6832 const ty_extra = ty_unwrapped.getExtra(ip);
6833 const ty_item = ty_unwrapped.getItem(ip);
6834 return switch (ty_item.tag) {
6835 .type_array_big => {
6836 const sentinel = @as(
6837 *const [1]Index,
6838 @ptrCast(&ty_extra.view().items(.@"0")[ty_item.data + std.meta.fieldIndex(Array, "sentinel").?]),
6839 );
6840 return .{ .aggregate = .{
6841 .ty = ty,
6842 .storage = .{ .elems = sentinel[0..@intFromBool(sentinel[0] != .none)] },
6843 } };
6844 },
6845 .type_array_small,
6846 .type_vector,
6847 .type_struct_packed_auto,
6848 .type_struct_packed_explicit,
6849 => .{ .aggregate = .{
6850 .ty = ty,
6851 .storage = .{ .elems = &.{} },
6852 } },
6853
6854 // There is only one possible value precisely due to the
6855 // fact that this values slice is fully populated!
6856 .type_struct,
6857 .type_struct_packed_auto_defaults,
6858 .type_struct_packed_explicit_defaults,
6859 => {
6860 const info = loadStructType(ip, ty);
6861 return .{ .aggregate = .{
6862 .ty = ty,
6863 .storage = .{ .elems = @ptrCast(info.field_defaults.get(ip)) },
6864 } };
6865 },
6866
6867 // There is only one possible value precisely due to the
6868 // fact that this values slice is fully populated!
6869 .type_tuple => {
6870 const type_tuple = extraDataTrail(ty_extra, TypeTuple, ty_item.data);
6871 const fields_len = type_tuple.data.fields_len;
6872 const values = ty_extra.view().items(.@"0")[type_tuple.end + fields_len ..][0..fields_len];
6873 return .{ .aggregate = .{
6874 .ty = ty,
6875 .storage = .{ .elems = @ptrCast(values) },
6876 } };
6877 },
6878
6879 else => unreachable,
6880 };
6881 },
6882 .bytes => {
6883 const extra = extraData(unwrapped_index.getExtra(ip), Bytes, data);
6884 return .{ .aggregate = .{
6885 .ty = extra.ty,
6886 .storage = .{ .bytes = extra.bytes },
6887 } };
6888 },
6889 .aggregate => {
6890 const extra_list = unwrapped_index.getExtra(ip);
6891 const extra = extraDataTrail(extra_list, Tag.Aggregate, data);
6892 const len: u32 = @intCast(ip.aggregateTypeLenIncludingSentinel(extra.data.ty));
6893 const fields: []const Index = @ptrCast(extra_list.view().items(.@"0")[extra.end..][0..len]);
6894 return .{ .aggregate = .{
6895 .ty = extra.data.ty,
6896 .storage = .{ .elems = fields },
6897 } };
6898 },
6899 .repeated => {
6900 const extra = extraData(unwrapped_index.getExtra(ip), Repeated, data);
6901 return .{ .aggregate = .{
6902 .ty = extra.ty,
6903 .storage = .{ .repeated_elem = extra.elem_val },
6904 } };
6905 },
6906 .union_value => .{ .un = extraData(unwrapped_index.getExtra(ip), Key.Union, data) },
6907 .error_set_error => .{ .err = extraData(unwrapped_index.getExtra(ip), Key.Error, data) },
6908 .error_union_error => {
6909 const extra = extraData(unwrapped_index.getExtra(ip), Key.Error, data);
6910 return .{ .error_union = .{
6911 .ty = extra.ty,
6912 .val = .{ .err_name = extra.name },
6913 } };
6914 },
6915 .error_union_payload => {
6916 const extra = extraData(unwrapped_index.getExtra(ip), Tag.TypeValue, data);
6917 return .{ .error_union = .{
6918 .ty = extra.ty,
6919 .val = .{ .payload = extra.val },
6920 } };
6921 },
6922 .enum_literal => .{ .enum_literal = @fromBackingInt(@intCast(data)) },
6923 .enum_tag => .{ .enum_tag = extraData(unwrapped_index.getExtra(ip), Tag.EnumTag, data) },
6924 .bitpack => .{ .bitpack = extraData(unwrapped_index.getExtra(ip), Key.Bitpack, data) },
6925
6926 .memoized_call => {
6927 const extra_list = unwrapped_index.getExtra(ip);
6928 const extra = extraDataTrail(extra_list, MemoizedCall, data);
6929 return .{ .memoized_call = .{
6930 .func = extra.data.func,
6931 .arg_values = @ptrCast(extra_list.view().items(.@"0")[extra.end..][0..extra.data.args_len]),
6932 .result = extra.data.result,
6933 .branch_count = extra.data.branch_count,
6934 .branch_quota = extra.data.branch_quota,
6935 } };
6936 },
6937 };
6938}
6939
6940fn extraErrorSet(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Key.ErrorSetType {
6941 const error_set = extraDataTrail(extra, Tag.ErrorSet, extra_index);
6942 return .{
6943 .names = .{
6944 .tid = tid,
6945 .start = @intCast(error_set.end),
6946 .len = error_set.data.names_len,
6947 },
6948 .names_map = error_set.data.names_map.toOptional(),
6949 };
6950}
6951
6952fn extraTypeTuple(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Key.TupleType {
6953 const type_tuple = extraDataTrail(extra, TypeTuple, extra_index);
6954 const fields_len = type_tuple.data.fields_len;
6955 return .{
6956 .types = .{
6957 .tid = tid,
6958 .start = type_tuple.end,
6959 .len = fields_len,
6960 },
6961 .values = .{
6962 .tid = tid,
6963 .start = type_tuple.end + fields_len,
6964 .len = fields_len,
6965 },
6966 };
6967}
6968
6969fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Key.FuncType {
6970 const type_function = extraDataTrail(extra, Tag.TypeFunction, extra_index);
6971 var trail_index: usize = type_function.end;
6972 const comptime_bits: u32 = if (!type_function.data.flags.has_comptime_bits) 0 else b: {
6973 const x = extra.view().items(.@"0")[trail_index];
6974 trail_index += 1;
6975 break :b x;
6976 };
6977 const noalias_bits: u32 = if (!type_function.data.flags.has_noalias_bits) 0 else b: {
6978 const x = extra.view().items(.@"0")[trail_index];
6979 trail_index += 1;
6980 break :b x;
6981 };
6982 const cc_extra_len = type_function.data.flags.cc.extraLen();
6983 const cc = type_function.data.flags.cc.unpack(extra.view().items(.@"0")[trail_index..][0..cc_extra_len]);
6984 trail_index += cc_extra_len;
6985 return .{
6986 .param_types = .{
6987 .tid = tid,
6988 .start = @intCast(trail_index),
6989 .len = type_function.data.params_len,
6990 },
6991 .return_type = type_function.data.return_type,
6992 .comptime_bits = comptime_bits,
6993 .noalias_bits = noalias_bits,
6994 .cc = cc,
6995 .is_var_args = type_function.data.flags.is_var_args,
6996 .is_noinline = type_function.data.flags.is_noinline,
6997 };
6998}
6999
7000fn extraFuncDecl(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Key.Func {
7001 const P = Tag.FuncDecl;
7002 const func_decl = extraDataTrail(extra, P, extra_index);
7003 return .{
7004 .tid = tid,
7005 .ty = func_decl.data.ty,
7006 .uncoerced_ty = func_decl.data.ty,
7007 .analysis_extra_index = extra_index + std.meta.fieldIndex(P, "analysis").?,
7008 .zir_body_inst_extra_index = extra_index + std.meta.fieldIndex(P, "zir_body_inst").?,
7009 .resolved_error_set_extra_index = if (func_decl.data.analysis.inferred_error_set) func_decl.end else 0,
7010 .branch_quota_extra_index = 0,
7011 .owner_nav = func_decl.data.owner_nav,
7012 .zir_body_inst = func_decl.data.zir_body_inst,
7013 .lbrace_line = func_decl.data.lbrace_line,
7014 .rbrace_line = func_decl.data.rbrace_line,
7015 .lbrace_column = func_decl.data.lbrace_column,
7016 .rbrace_column = func_decl.data.rbrace_column,
7017 .generic_owner = .none,
7018 .comptime_args = Index.Slice.empty,
7019 };
7020}
7021
7022fn extraFuncInstance(ip: *const InternPool, tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Key.Func {
7023 const extra_items = extra.view().items(.@"0");
7024 const analysis_extra_index = extra_index + std.meta.fieldIndex(Tag.FuncInstance, "analysis").?;
7025 const analysis: FuncAnalysis = @bitCast(@atomicLoad(u32, &extra_items[analysis_extra_index], .unordered));
7026 const owner_nav: Nav.Index = @fromBackingInt(@intCast(extra_items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "owner_nav").?]));
7027 const ty: Index = @fromBackingInt(@intCast(extra_items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "ty").?]));
7028 const generic_owner: Index = @fromBackingInt(@intCast(extra_items[extra_index + std.meta.fieldIndex(Tag.FuncInstance, "generic_owner").?]));
7029 const func_decl = ip.funcDeclInfo(generic_owner);
7030 const end_extra_index = extra_index + @as(u32, @typeInfo(Tag.FuncInstance).@"struct".field_names.len);
7031 return .{
7032 .tid = tid,
7033 .ty = ty,
7034 .uncoerced_ty = ty,
7035 .analysis_extra_index = analysis_extra_index,
7036 .zir_body_inst_extra_index = func_decl.zir_body_inst_extra_index,
7037 .resolved_error_set_extra_index = if (analysis.inferred_error_set) end_extra_index else 0,
7038 .branch_quota_extra_index = extra_index + std.meta.fieldIndex(Tag.FuncInstance, "branch_quota").?,
7039 .owner_nav = owner_nav,
7040 .zir_body_inst = func_decl.zir_body_inst,
7041 .lbrace_line = func_decl.lbrace_line,
7042 .rbrace_line = func_decl.rbrace_line,
7043 .lbrace_column = func_decl.lbrace_column,
7044 .rbrace_column = func_decl.rbrace_column,
7045 .generic_owner = generic_owner,
7046 .comptime_args = .{
7047 .tid = tid,
7048 .start = end_extra_index + @intFromBool(analysis.inferred_error_set),
7049 .len = ip.funcTypeParamsLen(func_decl.ty),
7050 },
7051 };
7052}
7053
7054fn extraFuncCoerced(ip: *const InternPool, extra: Local.Extra, extra_index: u32) Key.Func {
7055 const func_coerced = extraData(extra, Tag.FuncCoerced, extra_index);
7056 const func_unwrapped = func_coerced.func.unwrap(ip);
7057 const sub_item = func_unwrapped.getItem(ip);
7058 const func_extra = func_unwrapped.getExtra(ip);
7059 var func: Key.Func = switch (sub_item.tag) {
7060 .func_instance => ip.extraFuncInstance(func_unwrapped.tid, func_extra, sub_item.data),
7061 .func_decl => extraFuncDecl(func_unwrapped.tid, func_extra, sub_item.data),
7062 else => unreachable,
7063 };
7064 func.ty = func_coerced.ty;
7065 return func;
7066}
7067
7068fn indexToKeyBigInt(ip: *const InternPool, tid: Zcu.PerThread.Id, limb_index: u32, positive: bool) Key {
7069 const limbs_items = ip.getLocalShared(tid).getLimbs().view().items(.@"0");
7070 const int: Int = @bitCast(limbs_items[limb_index..][0..Int.limbs_items_len].*);
7071 const big_int: BigIntConst = .{
7072 .limbs = limbs_items[limb_index + Int.limbs_items_len ..][0..int.limbs_len],
7073 .positive = positive,
7074 };
7075 return .{ .int = .{
7076 .ty = int.ty,
7077 .storage = if (big_int.toInt(u64)) |x|
7078 .{ .u64 = x }
7079 else |_| if (big_int.toInt(i64)) |x|
7080 .{ .i64 = x }
7081 else |_|
7082 .{ .big_int = big_int },
7083 } };
7084}
7085
7086const GetOrPutKey = union(enum) {
7087 existing: Index,
7088 new: struct {
7089 ip: *InternPool,
7090 tid: Zcu.PerThread.Id,
7091 io: Io,
7092 shard: *Shard,
7093 map_index: u32,
7094 },
7095
7096 fn put(gop: *GetOrPutKey) Index {
7097 switch (gop.*) {
7098 .existing => unreachable,
7099 .new => |*info| {
7100 const index = Index.Unwrapped.wrap(.{
7101 .tid = info.tid,
7102 .index = info.ip.getLocal(info.tid).mutate.items.len - 1,
7103 }, info.ip);
7104 gop.putTentative(index);
7105 gop.putFinal(index);
7106 return index;
7107 },
7108 }
7109 }
7110
7111 fn putTentative(gop: *GetOrPutKey, index: Index) void {
7112 assert(index != .none);
7113 switch (gop.*) {
7114 .existing => unreachable,
7115 .new => |*info| gop.new.shard.shared.map.entries[info.map_index].release(index),
7116 }
7117 }
7118
7119 fn putFinal(gop: *GetOrPutKey, index: Index) void {
7120 assert(index != .none);
7121 switch (gop.*) {
7122 .existing => unreachable,
7123 .new => |info| {
7124 assert(info.shard.shared.map.entries[info.map_index].value == index);
7125 info.shard.mutate.map.len += 1;
7126 info.shard.mutate.map.mutex.unlock(info.io);
7127 gop.* = .{ .existing = index };
7128 },
7129 }
7130 }
7131
7132 fn cancel(gop: *GetOrPutKey) void {
7133 switch (gop.*) {
7134 .existing => {},
7135 .new => |info| info.shard.mutate.map.mutex.unlock(info.io),
7136 }
7137 gop.* = .{ .existing = undefined };
7138 }
7139
7140 fn deinit(gop: *GetOrPutKey) void {
7141 switch (gop.*) {
7142 .existing => {},
7143 .new => |info| info.shard.shared.map.entries[info.map_index].resetUnordered(),
7144 }
7145 gop.cancel();
7146 gop.* = undefined;
7147 }
7148};
7149fn getOrPutKey(
7150 ip: *InternPool,
7151 gpa: Allocator,
7152 io: Io,
7153 tid: Zcu.PerThread.Id,
7154 key: Key,
7155) Allocator.Error!GetOrPutKey {
7156 return ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, key, 0);
7157}
7158fn getOrPutKeyEnsuringAdditionalCapacity(
7159 ip: *InternPool,
7160 gpa: Allocator,
7161 io: Io,
7162 tid: Zcu.PerThread.Id,
7163 key: Key,
7164 additional_capacity: u32,
7165) Allocator.Error!GetOrPutKey {
7166 const full_hash = key.hash64(ip);
7167 const hash: u32 = @truncate(full_hash >> 32);
7168 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
7169 var map = shard.shared.map.acquire();
7170 const Map = @TypeOf(map);
7171 var map_mask = map.header().mask();
7172 var map_index = hash;
7173 while (true) : (map_index += 1) {
7174 map_index &= map_mask;
7175 const entry = &map.entries[map_index];
7176 const index = entry.acquire();
7177 if (index == .none) break;
7178 if (entry.hash != hash) continue;
7179 if (index.unwrap(ip).getTag(ip) == .removed) continue;
7180 if (ip.indexToKey(index).eql(key, ip)) return .{ .existing = index };
7181 }
7182 shard.mutate.map.mutex.lock(io, tid);
7183 errdefer shard.mutate.map.mutex.unlock(io);
7184 if (map.entries != shard.shared.map.entries) {
7185 map = shard.shared.map;
7186 map_mask = map.header().mask();
7187 map_index = hash;
7188 }
7189 while (true) : (map_index += 1) {
7190 map_index &= map_mask;
7191 const entry = &map.entries[map_index];
7192 const index = entry.value;
7193 if (index == .none) break;
7194 if (entry.hash != hash) continue;
7195 if (ip.indexToKey(index).eql(key, ip)) {
7196 defer shard.mutate.map.mutex.unlock(io);
7197 return .{ .existing = index };
7198 }
7199 }
7200 const map_header = map.header().*;
7201 const required = shard.mutate.map.len + additional_capacity;
7202 if (required >= map_header.capacity * 3 / 5) {
7203 const arena_state = &ip.getLocal(tid).mutate.arena;
7204 var arena = arena_state.promote(gpa);
7205 defer arena_state.* = arena.state;
7206 var new_map_capacity = map_header.capacity;
7207 while (true) {
7208 new_map_capacity *= 2;
7209 if (required < new_map_capacity * 3 / 5) break;
7210 }
7211 const new_map_buf = try arena.allocator().alignedAlloc(
7212 u8,
7213 .fromByteUnits(Map.alignment),
7214 Map.entries_offset + new_map_capacity * @sizeOf(Map.Entry),
7215 );
7216 const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) };
7217 new_map.header().* = .{ .capacity = new_map_capacity };
7218 @memset(new_map.entries[0..new_map_capacity], .{ .value = .none, .hash = undefined });
7219 const new_map_mask = new_map.header().mask();
7220 map_index = 0;
7221 while (map_index < map_header.capacity) : (map_index += 1) {
7222 const entry = &map.entries[map_index];
7223 const index = entry.value;
7224 if (index == .none) continue;
7225 const item_hash = entry.hash;
7226 var new_map_index = item_hash;
7227 while (true) : (new_map_index += 1) {
7228 new_map_index &= new_map_mask;
7229 const new_entry = &new_map.entries[new_map_index];
7230 if (new_entry.value != .none) continue;
7231 new_entry.* = .{
7232 .value = index,
7233 .hash = item_hash,
7234 };
7235 break;
7236 }
7237 }
7238 map = new_map;
7239 map_index = hash;
7240 while (true) : (map_index += 1) {
7241 map_index &= new_map_mask;
7242 if (map.entries[map_index].value == .none) break;
7243 }
7244 shard.shared.map.release(new_map);
7245 }
7246 map.entries[map_index].hash = hash;
7247 return .{ .new = .{
7248 .ip = ip,
7249 .tid = tid,
7250 .io = io,
7251 .shard = shard,
7252 .map_index = map_index,
7253 } };
7254}
7255
7256pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: Key) Allocator.Error!Index {
7257 var gop = try ip.getOrPutKey(gpa, io, tid, key);
7258 defer gop.deinit();
7259 if (gop == .existing) return gop.existing;
7260 const local = ip.getLocal(tid);
7261 const items = local.getMutableItems(gpa, io);
7262 const extra = local.getMutableExtra(gpa, io);
7263 try items.ensureUnusedCapacity(1);
7264 switch (key) {
7265 .int_type => |int_type| {
7266 if (int_type.signedness == .signed) assert(int_type.bits > 0);
7267 const t: Tag = switch (int_type.signedness) {
7268 .signed => .type_int_signed,
7269 .unsigned => .type_int_unsigned,
7270 };
7271 items.appendAssumeCapacity(.{
7272 .tag = t,
7273 .data = int_type.bits,
7274 });
7275 },
7276 .ptr_type => |ptr_type| {
7277 assert(ptr_type.child != .none);
7278 assert(ptr_type.sentinel == .none or ip.typeOf(ptr_type.sentinel) == ptr_type.child);
7279
7280 if (ptr_type.flags.size == .slice) {
7281 gop.cancel();
7282 var new_key = key;
7283 new_key.ptr_type.flags.size = .many;
7284 const ptr_type_index = try ip.get(gpa, io, tid, new_key);
7285 gop = try ip.getOrPutKey(gpa, io, tid, key);
7286
7287 try items.ensureUnusedCapacity(1);
7288 items.appendAssumeCapacity(.{
7289 .tag = .type_slice,
7290 .data = @backingInt(ptr_type_index),
7291 });
7292 return gop.put();
7293 }
7294
7295 var ptr_type_adjusted = ptr_type;
7296 if (ptr_type.flags.size == .c) ptr_type_adjusted.flags.is_allowzero = true;
7297
7298 items.appendAssumeCapacity(.{
7299 .tag = .type_pointer,
7300 .data = try addExtra(extra, ptr_type_adjusted),
7301 });
7302 },
7303 .array_type => |array_type| {
7304 assert(array_type.child != .none);
7305 assert(array_type.sentinel == .none or ip.typeOf(array_type.sentinel) == array_type.child);
7306
7307 if (std.math.cast(u32, array_type.len)) |len| {
7308 if (array_type.sentinel == .none) {
7309 items.appendAssumeCapacity(.{
7310 .tag = .type_array_small,
7311 .data = try addExtra(extra, Vector{
7312 .len = len,
7313 .child = array_type.child,
7314 }),
7315 });
7316 return gop.put();
7317 }
7318 }
7319
7320 const length = Array.Length.init(array_type.len);
7321 items.appendAssumeCapacity(.{
7322 .tag = .type_array_big,
7323 .data = try addExtra(extra, Array{
7324 .len0 = length.a,
7325 .len1 = length.b,
7326 .child = array_type.child,
7327 .sentinel = array_type.sentinel,
7328 }),
7329 });
7330 },
7331 .vector_type => |vector_type| {
7332 items.appendAssumeCapacity(.{
7333 .tag = .type_vector,
7334 .data = try addExtra(extra, Vector{
7335 .len = vector_type.len,
7336 .child = vector_type.child,
7337 }),
7338 });
7339 },
7340 .opt_type => |payload_type| {
7341 assert(payload_type != .none);
7342 items.appendAssumeCapacity(.{
7343 .tag = .type_optional,
7344 .data = @backingInt(payload_type),
7345 });
7346 },
7347 .anyframe_type => |payload_type| {
7348 // payload_type might be none, indicating the type is `anyframe`.
7349 items.appendAssumeCapacity(.{
7350 .tag = .type_anyframe,
7351 .data = @backingInt(payload_type),
7352 });
7353 },
7354 .error_union_type => |error_union_type| {
7355 items.appendAssumeCapacity(if (error_union_type.error_set_type == .anyerror_type) .{
7356 .tag = .type_anyerror_union,
7357 .data = @backingInt(error_union_type.payload_type),
7358 } else .{
7359 .tag = .type_error_union,
7360 .data = try addExtra(extra, error_union_type),
7361 });
7362 },
7363 .error_set_type => |error_set_type| {
7364 assert(error_set_type.names_map == .none);
7365 assert(std.sort.isSorted(NullTerminatedString, error_set_type.names.get(ip), {}, NullTerminatedString.indexLessThan));
7366 const names = error_set_type.names.get(ip);
7367 const names_map = try ip.addMap(gpa, io, tid, names.len);
7368 ip.addStringsToMap(names_map, names);
7369 const names_len = error_set_type.names.len;
7370 try extra.ensureUnusedCapacity(@typeInfo(Tag.ErrorSet).@"struct".field_names.len + names_len);
7371 items.appendAssumeCapacity(.{
7372 .tag = .type_error_set,
7373 .data = addExtraAssumeCapacity(extra, Tag.ErrorSet{
7374 .names_len = names_len,
7375 .names_map = names_map,
7376 }),
7377 });
7378 extra.appendSliceAssumeCapacity(.{@ptrCast(error_set_type.names.get(ip))});
7379 },
7380 .inferred_error_set_type => |ies_index| {
7381 items.appendAssumeCapacity(.{
7382 .tag = .type_inferred_error_set,
7383 .data = @backingInt(ies_index),
7384 });
7385 },
7386 .simple_type => |simple_type| {
7387 assert(@backingInt(simple_type) == items.mutate.len);
7388 items.appendAssumeCapacity(.{
7389 .tag = .simple_type,
7390 .data = 0, // avoid writing `undefined` bits to a file
7391 });
7392 },
7393 .simple_value => |simple_value| {
7394 assert(@backingInt(simple_value) == items.mutate.len);
7395 items.appendAssumeCapacity(.{
7396 .tag = .simple_value,
7397 .data = 0, // avoid writing `undefined` bits to a file
7398 });
7399 },
7400 .undef => |ty| {
7401 assert(ty != .none);
7402 items.appendAssumeCapacity(.{
7403 .tag = .undef,
7404 .data = @backingInt(ty),
7405 });
7406 },
7407
7408 .struct_type => unreachable, // instead use: getDeclaredStructType, getReifiedStructType
7409 .union_type => unreachable, // instead use: getDeclaredUnionType, getReifiedUnionType
7410 .enum_type => unreachable, // instead use: getDeclaredEnumType, getReifiedEnumType, getGeneratedEnumTagType
7411 .opaque_type => unreachable, // instead use: getDeclaredOpaqueType
7412 .spirv_type => unreachable, // instead use: getSpirvType
7413
7414 .tuple_type => unreachable, // use getTupleType() instead
7415 .func_type => unreachable, // use getFuncType() instead
7416 .@"extern" => unreachable, // use getExtern() instead
7417 .func => unreachable, // use getFuncInstance() or getFuncDecl() instead
7418 .un => unreachable, // use getUnion instead
7419
7420 .slice => |slice| {
7421 assert(ip.indexToKey(slice.ty).ptr_type.flags.size == .slice);
7422 assert(ip.indexToKey(ip.typeOf(slice.ptr)).ptr_type.flags.size == .many);
7423 items.appendAssumeCapacity(.{
7424 .tag = .ptr_slice,
7425 .data = try addExtra(extra, PtrSlice{
7426 .ty = slice.ty,
7427 .ptr = slice.ptr,
7428 .len = slice.len,
7429 }),
7430 });
7431 },
7432
7433 .ptr => |ptr| {
7434 const ptr_type = ip.indexToKey(ptr.ty).ptr_type;
7435 assert(ptr_type.flags.size != .slice);
7436 items.appendAssumeCapacity(switch (ptr.base_addr) {
7437 .nav => |nav| .{
7438 .tag = .ptr_nav,
7439 .data = try addExtra(extra, PtrNav.init(ptr.ty, nav, ptr.byte_offset)),
7440 },
7441 .comptime_alloc => |alloc_index| .{
7442 .tag = .ptr_comptime_alloc,
7443 .data = try addExtra(extra, PtrComptimeAlloc.init(ptr.ty, alloc_index, ptr.byte_offset)),
7444 },
7445 .uav => |uav| if (ptrsHaveSameAlignment(ip, ptr.ty, ptr_type, uav.orig_ty)) item: {
7446 if (ptr.ty != uav.orig_ty) {
7447 gop.cancel();
7448 var new_key = key;
7449 new_key.ptr.base_addr.uav.orig_ty = ptr.ty;
7450 gop = try ip.getOrPutKey(gpa, io, tid, new_key);
7451 if (gop == .existing) return gop.existing;
7452 }
7453 break :item .{
7454 .tag = .ptr_uav,
7455 .data = try addExtra(extra, PtrUav.init(ptr.ty, uav.val, ptr.byte_offset)),
7456 };
7457 } else .{
7458 .tag = .ptr_uav_aligned,
7459 .data = try addExtra(extra, PtrUavAligned.init(ptr.ty, uav.val, uav.orig_ty, ptr.byte_offset)),
7460 },
7461 .comptime_field => |field_val| item: {
7462 assert(field_val != .none);
7463 break :item .{
7464 .tag = .ptr_comptime_field,
7465 .data = try addExtra(extra, PtrComptimeField.init(ptr.ty, field_val, ptr.byte_offset)),
7466 };
7467 },
7468 .eu_payload, .opt_payload => |base| item: {
7469 switch (ptr.base_addr) {
7470 .eu_payload => assert(ip.indexToKey(
7471 ip.indexToKey(ip.typeOf(base)).ptr_type.child,
7472 ) == .error_union_type),
7473 .opt_payload => assert(ip.indexToKey(
7474 ip.indexToKey(ip.typeOf(base)).ptr_type.child,
7475 ) == .opt_type),
7476 else => unreachable,
7477 }
7478 break :item .{
7479 .tag = switch (ptr.base_addr) {
7480 .eu_payload => .ptr_eu_payload,
7481 .opt_payload => .ptr_opt_payload,
7482 else => unreachable,
7483 },
7484 .data = try addExtra(extra, PtrBase.init(ptr.ty, base, ptr.byte_offset)),
7485 };
7486 },
7487 .int => .{
7488 .tag = .ptr_int,
7489 .data = try addExtra(extra, PtrInt.init(ptr.ty, ptr.byte_offset)),
7490 },
7491 .arr_elem, .field => |base_index| {
7492 const base_ptr_type = ip.indexToKey(ip.typeOf(base_index.base)).ptr_type;
7493 switch (ptr.base_addr) {
7494 .arr_elem => assert(base_ptr_type.flags.size == .many),
7495 .field => {
7496 assert(base_ptr_type.flags.size == .one);
7497 switch (ip.indexToKey(base_ptr_type.child)) {
7498 .tuple_type => |tuple_type| {
7499 assert(ptr.base_addr == .field);
7500 assert(base_index.index < tuple_type.types.len);
7501 },
7502 .struct_type => {
7503 assert(ptr.base_addr == .field);
7504 assert(base_index.index < ip.loadStructType(base_ptr_type.child).field_types.len);
7505 },
7506 .union_type => {
7507 const union_type = ip.loadUnionType(base_ptr_type.child);
7508 assert(ptr.base_addr == .field);
7509 assert(base_index.index < union_type.field_types.len);
7510 },
7511 .ptr_type => |slice_type| {
7512 assert(ptr.base_addr == .field);
7513 assert(slice_type.flags.size == .slice);
7514 assert(base_index.index < 2);
7515 },
7516 else => unreachable,
7517 }
7518 },
7519 else => unreachable,
7520 }
7521 gop.cancel();
7522 const index_index = try ip.get(gpa, io, tid, .{ .int = .{
7523 .ty = .usize_type,
7524 .storage = .{ .u64 = base_index.index },
7525 } });
7526 gop = try ip.getOrPutKey(gpa, io, tid, key);
7527 try items.ensureUnusedCapacity(1);
7528 items.appendAssumeCapacity(.{
7529 .tag = switch (ptr.base_addr) {
7530 .arr_elem => .ptr_elem,
7531 .field => .ptr_field,
7532 else => unreachable,
7533 },
7534 .data = try addExtra(extra, PtrBaseIndex.init(ptr.ty, base_index.base, index_index, ptr.byte_offset)),
7535 });
7536 return gop.put();
7537 },
7538 });
7539 },
7540
7541 .opt => |opt| {
7542 assert(ip.isOptionalType(opt.ty));
7543 assert(opt.val == .none or ip.indexToKey(opt.ty).opt_type == ip.typeOf(opt.val));
7544 items.appendAssumeCapacity(if (opt.val == .none) .{
7545 .tag = .opt_null,
7546 .data = @backingInt(opt.ty),
7547 } else .{
7548 .tag = .opt_payload,
7549 .data = try addExtra(extra, Tag.TypeValue{
7550 .ty = opt.ty,
7551 .val = opt.val,
7552 }),
7553 });
7554 },
7555
7556 .int => |int| b: {
7557 assert(ip.isIntegerType(int.ty));
7558 switch (int.ty) {
7559 .u8_type => switch (int.storage) {
7560 .big_int => |big_int| {
7561 items.appendAssumeCapacity(.{
7562 .tag = .int_u8,
7563 .data = big_int.toInt(u8) catch unreachable,
7564 });
7565 break :b;
7566 },
7567 inline .u64, .i64 => |x| {
7568 items.appendAssumeCapacity(.{
7569 .tag = .int_u8,
7570 .data = @as(u8, @intCast(x)),
7571 });
7572 break :b;
7573 },
7574 },
7575 .u16_type => switch (int.storage) {
7576 .big_int => |big_int| {
7577 items.appendAssumeCapacity(.{
7578 .tag = .int_u16,
7579 .data = big_int.toInt(u16) catch unreachable,
7580 });
7581 break :b;
7582 },
7583 inline .u64, .i64 => |x| {
7584 items.appendAssumeCapacity(.{
7585 .tag = .int_u16,
7586 .data = @as(u16, @intCast(x)),
7587 });
7588 break :b;
7589 },
7590 },
7591 .u32_type => switch (int.storage) {
7592 .big_int => |big_int| {
7593 items.appendAssumeCapacity(.{
7594 .tag = .int_u32,
7595 .data = big_int.toInt(u32) catch unreachable,
7596 });
7597 break :b;
7598 },
7599 inline .u64, .i64 => |x| {
7600 items.appendAssumeCapacity(.{
7601 .tag = .int_u32,
7602 .data = @as(u32, @intCast(x)),
7603 });
7604 break :b;
7605 },
7606 },
7607 .i32_type => switch (int.storage) {
7608 .big_int => |big_int| {
7609 const casted = big_int.toInt(i32) catch unreachable;
7610 items.appendAssumeCapacity(.{
7611 .tag = .int_i32,
7612 .data = @as(u32, @bitCast(casted)),
7613 });
7614 break :b;
7615 },
7616 inline .u64, .i64 => |x| {
7617 items.appendAssumeCapacity(.{
7618 .tag = .int_i32,
7619 .data = @as(u32, @bitCast(@as(i32, @intCast(x)))),
7620 });
7621 break :b;
7622 },
7623 },
7624 .usize_type => switch (int.storage) {
7625 .big_int => |big_int| {
7626 if (big_int.toInt(u32)) |casted| {
7627 items.appendAssumeCapacity(.{
7628 .tag = .int_usize,
7629 .data = casted,
7630 });
7631 break :b;
7632 } else |_| {}
7633 },
7634 inline .u64, .i64 => |x| {
7635 if (std.math.cast(u32, x)) |casted| {
7636 items.appendAssumeCapacity(.{
7637 .tag = .int_usize,
7638 .data = casted,
7639 });
7640 break :b;
7641 }
7642 },
7643 },
7644 .comptime_int_type => switch (int.storage) {
7645 .big_int => |big_int| {
7646 if (big_int.toInt(u32)) |casted| {
7647 items.appendAssumeCapacity(.{
7648 .tag = .int_comptime_int_u32,
7649 .data = casted,
7650 });
7651 break :b;
7652 } else |_| {}
7653 if (big_int.toInt(i32)) |casted| {
7654 items.appendAssumeCapacity(.{
7655 .tag = .int_comptime_int_i32,
7656 .data = @as(u32, @bitCast(casted)),
7657 });
7658 break :b;
7659 } else |_| {}
7660 },
7661 inline .u64, .i64 => |x| {
7662 if (std.math.cast(u32, x)) |casted| {
7663 items.appendAssumeCapacity(.{
7664 .tag = .int_comptime_int_u32,
7665 .data = casted,
7666 });
7667 break :b;
7668 }
7669 if (std.math.cast(i32, x)) |casted| {
7670 items.appendAssumeCapacity(.{
7671 .tag = .int_comptime_int_i32,
7672 .data = @as(u32, @bitCast(casted)),
7673 });
7674 break :b;
7675 }
7676 },
7677 },
7678 else => {},
7679 }
7680 switch (int.storage) {
7681 .big_int => |big_int| {
7682 if (big_int.toInt(u32)) |casted| {
7683 items.appendAssumeCapacity(.{
7684 .tag = .int_small,
7685 .data = try addExtra(extra, IntSmall{
7686 .ty = int.ty,
7687 .value = casted,
7688 }),
7689 });
7690 return gop.put();
7691 } else |_| {}
7692
7693 const tag: Tag = if (big_int.positive or big_int.eqlZero()) .int_positive else .int_negative;
7694 try addInt(ip, gpa, io, tid, int.ty, tag, big_int.limbs);
7695 },
7696 inline .u64, .i64 => |x| {
7697 if (std.math.cast(u32, x)) |casted| {
7698 items.appendAssumeCapacity(.{
7699 .tag = .int_small,
7700 .data = try addExtra(extra, IntSmall{
7701 .ty = int.ty,
7702 .value = casted,
7703 }),
7704 });
7705 return gop.put();
7706 }
7707
7708 var buf: [2]Limb = undefined;
7709 const big_int = BigIntMutable.init(&buf, x).toConst();
7710 const tag: Tag = if (big_int.positive or big_int.eqlZero()) .int_positive else .int_negative;
7711 try addInt(ip, gpa, io, tid, int.ty, tag, big_int.limbs);
7712 },
7713 }
7714 },
7715
7716 .err => |err| {
7717 assert(ip.isErrorSetType(err.ty));
7718 items.appendAssumeCapacity(.{
7719 .tag = .error_set_error,
7720 .data = try addExtra(extra, err),
7721 });
7722 },
7723
7724 .error_union => |error_union| {
7725 assert(ip.isErrorUnionType(error_union.ty));
7726 items.appendAssumeCapacity(switch (error_union.val) {
7727 .err_name => |err_name| .{
7728 .tag = .error_union_error,
7729 .data = try addExtra(extra, Key.Error{
7730 .ty = error_union.ty,
7731 .name = err_name,
7732 }),
7733 },
7734 .payload => |payload| .{
7735 .tag = .error_union_payload,
7736 .data = try addExtra(extra, Tag.TypeValue{
7737 .ty = error_union.ty,
7738 .val = payload,
7739 }),
7740 },
7741 });
7742 },
7743
7744 .enum_literal => |enum_literal| items.appendAssumeCapacity(.{
7745 .tag = .enum_literal,
7746 .data = @backingInt(enum_literal),
7747 }),
7748
7749 .enum_tag => |enum_tag| {
7750 const enum_obj = ip.loadEnumType(enum_tag.ty);
7751 assert(ip.typeOf(enum_tag.int) == enum_obj.int_tag_type);
7752 items.appendAssumeCapacity(.{
7753 .tag = .enum_tag,
7754 .data = try addExtra(extra, enum_tag),
7755 });
7756 },
7757
7758 .float => |float| {
7759 switch (float.ty) {
7760 .f16_type => items.appendAssumeCapacity(.{
7761 .tag = .float_f16,
7762 .data = @as(u16, @bitCast(float.storage.f16)),
7763 }),
7764 .f32_type => items.appendAssumeCapacity(.{
7765 .tag = .float_f32,
7766 .data = @as(u32, @bitCast(float.storage.f32)),
7767 }),
7768 .f64_type => items.appendAssumeCapacity(.{
7769 .tag = .float_f64,
7770 .data = try addExtra(extra, Float64.pack(float.storage.f64)),
7771 }),
7772 .f80_type => items.appendAssumeCapacity(.{
7773 .tag = .float_f80,
7774 .data = try addExtra(extra, Float80.pack(float.storage.f80)),
7775 }),
7776 .f128_type => items.appendAssumeCapacity(.{
7777 .tag = .float_f128,
7778 .data = try addExtra(extra, Float128.pack(float.storage.f128)),
7779 }),
7780 .c_longdouble_type => switch (float.storage) {
7781 .f80 => |x| items.appendAssumeCapacity(.{
7782 .tag = .float_c_longdouble_f80,
7783 .data = try addExtra(extra, Float80.pack(x)),
7784 }),
7785 inline .f16, .f32, .f64, .f128 => |x| items.appendAssumeCapacity(.{
7786 .tag = .float_c_longdouble_f128,
7787 .data = try addExtra(extra, Float128.pack(x)),
7788 }),
7789 },
7790 .comptime_float_type => items.appendAssumeCapacity(.{
7791 .tag = .float_comptime_float,
7792 .data = try addExtra(extra, Float128.pack(float.storage.f128)),
7793 }),
7794 else => unreachable,
7795 }
7796 },
7797
7798 .aggregate => |aggregate| {
7799 const ty_key = ip.indexToKey(aggregate.ty);
7800 const len = ip.aggregateTypeLen(aggregate.ty);
7801 const child: Index, const sentinel: Index = switch (ty_key) {
7802 .array_type => |array_type| .{ array_type.child, array_type.sentinel },
7803 .vector_type => |vector_type| .{ vector_type.child, .none },
7804 .tuple_type => .{ .none, .none },
7805 .struct_type => child: {
7806 assert(ip.loadStructType(aggregate.ty).layout != .@"packed");
7807 break :child .{ .none, .none };
7808 },
7809 else => unreachable,
7810 };
7811 const len_including_sentinel = len + @intFromBool(sentinel != .none);
7812 switch (aggregate.storage) {
7813 .bytes => |bytes| {
7814 assert(child == .u8_type);
7815 if (sentinel != .none) {
7816 assert(bytes.at(@intCast(len), ip) == ip.indexToKey(sentinel).int.storage.u64);
7817 }
7818 },
7819 .elems => |elems| {
7820 if (elems.len != len) {
7821 assert(elems.len == len_including_sentinel);
7822 assert(elems[@intCast(len)] == sentinel);
7823 }
7824 },
7825 .repeated_elem => |elem| {
7826 assert(sentinel == .none or elem == sentinel);
7827 },
7828 }
7829 if (aggregate.storage.values().len > 0) switch (ty_key) {
7830 .array_type, .vector_type => {
7831 var any_defined = false;
7832 for (aggregate.storage.values()) |elem| {
7833 if (!ip.isUndef(elem)) any_defined = true;
7834 assert(ip.typeOf(elem) == child);
7835 }
7836 assert(any_defined); // aggregate fields must not be all undefined
7837 },
7838 .struct_type => {
7839 var any_defined = false;
7840 for (aggregate.storage.values(), ip.loadStructType(aggregate.ty).field_types.get(ip)) |elem, field_ty| {
7841 if (!ip.isUndef(elem)) any_defined = true;
7842 assert(ip.typeOf(elem) == field_ty);
7843 }
7844 assert(any_defined); // aggregate fields must not be all undefined
7845 },
7846 .tuple_type => |tuple_type| {
7847 var any_defined = false;
7848 for (aggregate.storage.values(), tuple_type.types.get(ip)) |elem, ty| {
7849 if (!ip.isUndef(elem)) any_defined = true;
7850 assert(ip.typeOf(elem) == ty);
7851 }
7852 assert(any_defined); // aggregate fields must not be all undefined
7853 },
7854 else => unreachable,
7855 };
7856
7857 if (len == 0) {
7858 items.appendAssumeCapacity(.{
7859 .tag = .only_possible_value,
7860 .data = @backingInt(aggregate.ty),
7861 });
7862 return gop.put();
7863 }
7864
7865 switch (ty_key) {
7866 .tuple_type => |tuple_type| opv: {
7867 switch (aggregate.storage) {
7868 .bytes => |bytes| for (tuple_type.values.get(ip), bytes.at(0, ip)..) |value, byte| {
7869 if (value == .none) break :opv;
7870 switch (ip.indexToKey(value)) {
7871 .undef => break :opv,
7872 .int => |int| switch (int.storage) {
7873 .u64 => |x| if (x != byte) break :opv,
7874 else => break :opv,
7875 },
7876 else => unreachable,
7877 }
7878 },
7879 .elems => |elems| if (!std.mem.eql(
7880 Index,
7881 tuple_type.values.get(ip),
7882 elems,
7883 )) break :opv,
7884 .repeated_elem => |elem| for (tuple_type.values.get(ip)) |value| {
7885 if (value != elem) break :opv;
7886 },
7887 }
7888 // This encoding works thanks to the fact that, as we just verified,
7889 // the type itself contains a slice of values that can be provided
7890 // in the aggregate fields.
7891 items.appendAssumeCapacity(.{
7892 .tag = .only_possible_value,
7893 .data = @backingInt(aggregate.ty),
7894 });
7895 return gop.put();
7896 },
7897 else => {},
7898 }
7899
7900 repeated: {
7901 switch (aggregate.storage) {
7902 .bytes => |bytes| for (bytes.toSlice(len, ip)[1..]) |byte|
7903 if (byte != bytes.at(0, ip)) break :repeated,
7904 .elems => |elems| for (elems[1..@intCast(len)]) |elem|
7905 if (elem != elems[0]) break :repeated,
7906 .repeated_elem => {},
7907 }
7908 const elem = switch (aggregate.storage) {
7909 .bytes => |bytes| elem: {
7910 gop.cancel();
7911 const elem = try ip.get(gpa, io, tid, .{ .int = .{
7912 .ty = .u8_type,
7913 .storage = .{ .u64 = bytes.at(0, ip) },
7914 } });
7915 gop = try ip.getOrPutKey(gpa, io, tid, key);
7916 try items.ensureUnusedCapacity(1);
7917 break :elem elem;
7918 },
7919 .elems => |elems| elems[0],
7920 .repeated_elem => |elem| elem,
7921 };
7922
7923 try extra.ensureUnusedCapacity(@typeInfo(Repeated).@"struct".field_names.len);
7924 items.appendAssumeCapacity(.{
7925 .tag = .repeated,
7926 .data = addExtraAssumeCapacity(extra, Repeated{
7927 .ty = aggregate.ty,
7928 .elem_val = elem,
7929 }),
7930 });
7931 return gop.put();
7932 }
7933
7934 if (child == .u8_type) bytes: {
7935 const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
7936 const start = string_bytes.mutate.len;
7937 try string_bytes.ensureUnusedCapacity(@intCast(len_including_sentinel + 1));
7938 try extra.ensureUnusedCapacity(@typeInfo(Bytes).@"struct".field_names.len);
7939 switch (aggregate.storage) {
7940 .bytes => |bytes| string_bytes.appendSliceAssumeCapacity(.{bytes.toSlice(len, ip)}),
7941 .elems => |elems| for (elems[0..@intCast(len)]) |elem| switch (ip.indexToKey(elem)) {
7942 .undef => {
7943 string_bytes.shrinkRetainingCapacity(start);
7944 break :bytes;
7945 },
7946 .int => |int| string_bytes.appendAssumeCapacity(.{@intCast(int.storage.u64)}),
7947 else => unreachable,
7948 },
7949 .repeated_elem => |elem| switch (ip.indexToKey(elem)) {
7950 .undef => break :bytes,
7951 .int => |int| @memset(
7952 string_bytes.addManyAsSliceAssumeCapacity(@intCast(len))[0],
7953 @intCast(int.storage.u64),
7954 ),
7955 else => unreachable,
7956 },
7957 }
7958 if (sentinel != .none) string_bytes.appendAssumeCapacity(.{
7959 @intCast(ip.indexToKey(sentinel).int.storage.u64),
7960 });
7961 const string = try ip.getOrPutTrailingString(
7962 gpa,
7963 io,
7964 tid,
7965 @intCast(len_including_sentinel),
7966 .maybe_embedded_nulls,
7967 );
7968 items.appendAssumeCapacity(.{
7969 .tag = .bytes,
7970 .data = addExtraAssumeCapacity(extra, Bytes{
7971 .ty = aggregate.ty,
7972 .bytes = string,
7973 }),
7974 });
7975 return gop.put();
7976 }
7977
7978 try extra.ensureUnusedCapacity(
7979 @typeInfo(Tag.Aggregate).@"struct".field_names.len + @as(usize, @intCast(len_including_sentinel + 1)),
7980 );
7981 items.appendAssumeCapacity(.{
7982 .tag = .aggregate,
7983 .data = addExtraAssumeCapacity(extra, Tag.Aggregate{
7984 .ty = aggregate.ty,
7985 }),
7986 });
7987 extra.appendSliceAssumeCapacity(.{@ptrCast(aggregate.storage.elems)});
7988 if (sentinel != .none) extra.appendAssumeCapacity(.{@backingInt(sentinel)});
7989 },
7990 .bitpack => |bitpack| {
7991 switch (ip.zigTypeTag(bitpack.ty)) {
7992 .@"struct" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadStructType(bitpack.ty).packed_backing_int_type),
7993 .@"union" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadUnionType(bitpack.ty).packed_backing_int_type),
7994 else => unreachable,
7995 }
7996 assert(!ip.isUndef(bitpack.backing_int_val));
7997 items.appendAssumeCapacity(.{
7998 .tag = .bitpack,
7999 .data = try addExtra(extra, bitpack),
8000 });
8001 },
8002
8003 .memoized_call => |memoized_call| {
8004 for (memoized_call.arg_values) |arg| assert(arg != .none);
8005 try extra.ensureUnusedCapacity(@typeInfo(MemoizedCall).@"struct".field_names.len +
8006 memoized_call.arg_values.len);
8007 items.appendAssumeCapacity(.{
8008 .tag = .memoized_call,
8009 .data = addExtraAssumeCapacity(extra, MemoizedCall{
8010 .func = memoized_call.func,
8011 .args_len = @intCast(memoized_call.arg_values.len),
8012 .result = memoized_call.result,
8013 .branch_count = memoized_call.branch_count,
8014 .branch_quota = memoized_call.branch_quota,
8015 }),
8016 });
8017 extra.appendSliceAssumeCapacity(.{@ptrCast(memoized_call.arg_values)});
8018 },
8019 }
8020 return gop.put();
8021}
8022
8023pub fn getDeclaredStructType(
8024 ip: *InternPool,
8025 gpa: Allocator,
8026 io: Io,
8027 tid: Zcu.PerThread.Id,
8028 ini: struct {
8029 zir_index: TrackedInst.Index,
8030 captures: []const CaptureValue,
8031
8032 // If the value of any of the following fields would change on an incremental update, then logic
8033 // in `Zcu.mapOldZirToNew` must detect that (these properties are all trivially known from ZIR)
8034 // and refuse to map the type declaration. This causes `zir_index` to change so that a new type
8035 // will be interned at a fresh index.
8036 //
8037 // In the future, it would be good to remove all of those fields from `ini`, and in fact just
8038 // have a single function `getDeclaredContainer` which is suitable for all container types.
8039 // However, this requires some major changes to how container types are represented in the
8040 // InternPool, so that it is possible for their backing storage to be "reallocated" as needed
8041 // during type resolution.
8042 fields_len: u32,
8043 layout: std.lang.Type.ContainerLayout,
8044 any_comptime_fields: bool,
8045 any_field_defaults: bool,
8046 any_field_aligns: bool,
8047 packed_backing_mode: BackingTypeMode,
8048 },
8049) Allocator.Error!WipContainerType.Result {
8050 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .struct_type = .{ .declared = .{
8051 .zir_index = ini.zir_index,
8052 .captures = .{ .external = ini.captures },
8053 } } });
8054 defer gop.deinit();
8055 if (gop == .existing) return .{ .existing = gop.existing };
8056
8057 const local = ip.getLocal(tid);
8058 const items = local.getMutableItems(gpa, io);
8059 const extra = local.getMutableExtra(gpa, io);
8060 try items.ensureUnusedCapacity(1);
8061
8062 const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
8063 errdefer local.mutate.maps.len -= 1;
8064
8065 const is_extern = switch (ini.layout) {
8066 .auto => false,
8067 .@"extern" => true,
8068 .@"packed" => {
8069 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStructPacked).@"struct".field_names.len +
8070 ini.captures.len + // capture
8071 ini.fields_len + // field_name
8072 ini.fields_len + // field_type
8073 (if (ini.any_field_defaults) ini.fields_len else 0)); // field_default
8074
8075 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{
8076 .zir_index = ini.zir_index,
8077 .bits = .{
8078 .captures_len = @fromBackingInt(@intCast(ini.captures.len)),
8079 .want_layout = false,
8080 },
8081 .name = undefined, // set by `finish`
8082 .fqn = undefined, // set by `finish`
8083 .name_nav = undefined, // set by `finish`
8084 .namespace = undefined, // set by `finish`
8085 .backing_int_type = .none,
8086 .fields_len = ini.fields_len,
8087 .field_name_map = field_name_map,
8088 });
8089 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture
8090 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8091 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8092 if (ini.any_field_defaults) {
8093 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_default
8094 }
8095 items.appendAssumeCapacity(.{
8096 .tag = switch (ini.packed_backing_mode) {
8097 .auto => if (ini.any_field_defaults) .type_struct_packed_auto_defaults else .type_struct_packed_auto,
8098 .explicit => if (ini.any_field_defaults) .type_struct_packed_explicit_defaults else .type_struct_packed_explicit,
8099 },
8100 .data = extra_index,
8101 });
8102 return .{ .wip = .{
8103 .index = gop.put(),
8104 .tid = tid,
8105 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?,
8106 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "fqn").?,
8107 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?,
8108 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?,
8109 .field_names = undefined,
8110 .field_types = undefined,
8111 .field_values = undefined,
8112 .field_aligns = undefined,
8113 .field_is_comptime_bits = undefined,
8114 } };
8115 },
8116 };
8117
8118 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStruct).@"struct".field_names.len +
8119 1 + // captures_len
8120 ini.captures.len + // capture
8121 ini.fields_len + // field_name
8122 ini.fields_len + // field_type
8123 (if (ini.any_field_defaults) ini.fields_len else 0) + // field_default
8124 (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0) + // field_align
8125 (if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0) + // field_is_comptime_bits
8126 (if (!is_extern) ini.fields_len else 0) + // field_runtime_order
8127 ini.fields_len); // field_offset
8128
8129 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{
8130 .zir_index = ini.zir_index,
8131 .name = undefined, // set by `finish`
8132 .fqn = undefined, // set by `finish`
8133 .name_nav = undefined, // set by `finish`
8134 .namespace = undefined, // set by `finish`
8135 .fields_len = ini.fields_len,
8136 .field_name_map = field_name_map,
8137 .size = 0,
8138 .flags = .{
8139 .any_captures = if (ini.captures.len != 0) .true else .false,
8140 .layout = if (is_extern) .@"extern" else .auto,
8141 .any_comptime_fields = ini.any_comptime_fields,
8142 .any_field_defaults = ini.any_field_defaults,
8143 .any_field_aligns = ini.any_field_aligns,
8144 .class = .no_possible_value,
8145 .alignment = .none,
8146 .want_layout = false,
8147 },
8148 });
8149 if (ini.captures.len != 0) {
8150 extra.appendAssumeCapacity(.{@intCast(ini.captures.len)}); // captures_len
8151 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture
8152 }
8153 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8154 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8155 if (ini.any_field_defaults) {
8156 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_default
8157 }
8158 if (ini.any_field_aligns) {
8159 extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align
8160 }
8161 if (ini.any_comptime_fields) {
8162 extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 31) / 32); // field_is_comptime_bits
8163 }
8164 if (!is_extern) {
8165 extra.appendNTimesAssumeCapacity(.{@backingInt(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len); // field_runtime_order
8166 }
8167 extra.appendNTimesAssumeCapacity(.{0}, ini.fields_len); // field_offset
8168 items.appendAssumeCapacity(.{
8169 .tag = .type_struct,
8170 .data = extra_index,
8171 });
8172 return .{ .wip = .{
8173 .index = gop.put(),
8174 .tid = tid,
8175 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?,
8176 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "fqn").?,
8177 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?,
8178 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?,
8179 .field_names = undefined,
8180 .field_types = undefined,
8181 .field_values = undefined,
8182 .field_aligns = undefined,
8183 .field_is_comptime_bits = undefined,
8184 } };
8185}
8186
8187pub fn getReifiedStructType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
8188 zir_index: TrackedInst.Index,
8189 type_hash: u64,
8190 fields_len: u32,
8191 layout: std.lang.Type.ContainerLayout,
8192 any_comptime_fields: bool,
8193 any_field_defaults: bool,
8194 any_field_aligns: bool,
8195 /// Explicitly specified backing int type. `.none` if not packed or if backing type is inferred.
8196 packed_backing_int_type: Index,
8197}) Allocator.Error!WipContainerType.Result {
8198 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .struct_type = .{ .reified = .{
8199 .zir_index = ini.zir_index,
8200 .type_hash = ini.type_hash,
8201 } } });
8202 defer gop.deinit();
8203 if (gop == .existing) return .{ .existing = gop.existing };
8204
8205 const local = ip.getLocal(tid);
8206 const items = local.getMutableItems(gpa, io);
8207 const extra = local.getMutableExtra(gpa, io);
8208 try items.ensureUnusedCapacity(1);
8209
8210 const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
8211 errdefer local.mutate.maps.len -= 1;
8212
8213 const is_extern = switch (ini.layout) {
8214 .auto => false,
8215 .@"extern" => true,
8216 .@"packed" => {
8217 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStructPacked).@"struct".field_names.len +
8218 2 + // type_hash
8219 ini.fields_len + // field_name
8220 ini.fields_len + // field_type
8221 (if (ini.any_field_defaults) ini.fields_len else 0)); // field_default
8222
8223 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{
8224 .zir_index = ini.zir_index,
8225 .bits = .{
8226 .captures_len = .reified,
8227 .want_layout = false,
8228 },
8229 .name = undefined, // set by `finish`
8230 .fqn = undefined, // set by `finish`
8231 .name_nav = undefined, // set by `finish`
8232 .namespace = undefined, // set by `finish`
8233 .backing_int_type = ini.packed_backing_int_type,
8234 .fields_len = ini.fields_len,
8235 .field_name_map = field_name_map,
8236 });
8237 _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash
8238 const field_names_start = extra.mutate.len;
8239 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8240 const field_types_start = extra.mutate.len;
8241 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8242 const field_defaults_start = extra.mutate.len;
8243 if (ini.any_field_defaults) {
8244 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_default
8245 }
8246 items.appendAssumeCapacity(.{
8247 .tag = switch (ini.packed_backing_int_type) {
8248 .none => if (ini.any_field_defaults) .type_struct_packed_auto_defaults else .type_struct_packed_auto,
8249 else => if (ini.any_field_defaults) .type_struct_packed_explicit_defaults else .type_struct_packed_explicit,
8250 },
8251 .data = extra_index,
8252 });
8253 return .{ .wip = .{
8254 .index = gop.put(),
8255 .tid = tid,
8256 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?,
8257 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "fqn").?,
8258 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name_nav").?,
8259 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?,
8260 .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len },
8261 .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len },
8262 .field_values = if (ini.any_field_defaults)
8263 .{ .tid = tid, .start = field_defaults_start, .len = ini.fields_len }
8264 else
8265 undefined,
8266 .field_aligns = undefined,
8267 .field_is_comptime_bits = undefined,
8268 } };
8269 },
8270 };
8271
8272 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeStruct).@"struct".field_names.len +
8273 2 + // type_hash
8274 ini.fields_len + // field_name
8275 ini.fields_len + // field_type
8276 (if (ini.any_field_defaults) ini.fields_len else 0) + // field_default
8277 (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0) + // field_align
8278 (if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0) + // field_is_comptime_bits
8279 (if (!is_extern) ini.fields_len else 0) + // field_runtime_order
8280 ini.fields_len); // field_offset
8281
8282 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{
8283 .zir_index = ini.zir_index,
8284 .name = undefined, // set by `finish`
8285 .fqn = undefined, // set by `finish`
8286 .name_nav = undefined, // set by `finish`
8287 .namespace = undefined, // set by `finish`
8288 .fields_len = ini.fields_len,
8289 .field_name_map = field_name_map,
8290 .size = 0,
8291 .flags = .{
8292 .any_captures = .reified,
8293 .layout = if (is_extern) .@"extern" else .auto,
8294 .any_comptime_fields = ini.any_comptime_fields,
8295 .any_field_defaults = ini.any_field_defaults,
8296 .any_field_aligns = ini.any_field_aligns,
8297 .class = .no_possible_value,
8298 .alignment = .none,
8299 .want_layout = false,
8300 },
8301 });
8302 _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash
8303 const field_names_start = extra.mutate.len;
8304 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8305 const field_types_start = extra.mutate.len;
8306 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8307 const field_defaults_start = extra.mutate.len;
8308 if (ini.any_field_defaults) {
8309 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_default
8310 }
8311 const field_aligns_start = extra.mutate.len;
8312 if (ini.any_field_aligns) {
8313 extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align
8314 }
8315 const field_is_comptime_bits_start = extra.mutate.len;
8316 if (ini.any_comptime_fields) {
8317 extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 31) / 32); // field_is_comptime_bits
8318 }
8319 if (!is_extern) {
8320 extra.appendNTimesAssumeCapacity(.{@backingInt(LoadedStructType.RuntimeOrder.unresolved)}, ini.fields_len); // field_runtime_order
8321 }
8322 extra.appendNTimesAssumeCapacity(.{0}, ini.fields_len); // field_offset
8323 items.appendAssumeCapacity(.{
8324 .tag = .type_struct,
8325 .data = extra_index,
8326 });
8327 return .{ .wip = .{
8328 .index = gop.put(),
8329 .tid = tid,
8330 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?,
8331 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "fqn").?,
8332 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name_nav").?,
8333 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?,
8334 .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len },
8335 .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len },
8336 .field_values = if (ini.any_field_defaults)
8337 .{ .tid = tid, .start = field_defaults_start, .len = ini.fields_len }
8338 else
8339 undefined,
8340 .field_aligns = if (ini.any_field_aligns)
8341 .{ .tid = tid, .start = field_aligns_start, .len = ini.fields_len }
8342 else
8343 undefined,
8344 .field_is_comptime_bits = if (ini.any_comptime_fields)
8345 .{ .tid = tid, .start = field_is_comptime_bits_start, .len = (ini.fields_len + 31) / 32 }
8346 else
8347 undefined,
8348 } };
8349}
8350
8351pub fn getDeclaredUnionType(
8352 ip: *InternPool,
8353 gpa: Allocator,
8354 io: Io,
8355 tid: Zcu.PerThread.Id,
8356 ini: struct {
8357 zir_index: TrackedInst.Index,
8358 captures: []const CaptureValue,
8359
8360 // If the value of any of the following fields would change on an incremental update, then logic
8361 // in `Zcu.mapOldZirToNew` must detect that (these properties are all trivially known from ZIR)
8362 // and refuse to map the type declaration. This causes `zir_index` to change so that a new type
8363 // will be interned at a fresh index.
8364 //
8365 // In the future, it would be good to remove all of those fields from `ini`, and in fact just
8366 // have a single function `getDeclaredContainer` which is suitable for all container types.
8367 // However, this requires some major changes to how container types are represented in the
8368 // InternPool, so that it is possible for their backing storage to be "reallocated" as needed
8369 // during type resolution.
8370 fields_len: u32,
8371 layout: std.lang.Type.ContainerLayout,
8372 any_field_aligns: bool,
8373 tag_usage: LoadedUnionType.TagUsage,
8374 enum_tag_mode: BackingTypeMode,
8375 packed_backing_mode: BackingTypeMode,
8376 },
8377) Allocator.Error!WipContainerType.Result {
8378 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .union_type = .{ .declared = .{
8379 .zir_index = ini.zir_index,
8380 .captures = .{ .external = ini.captures },
8381 } } });
8382 defer gop.deinit();
8383 if (gop == .existing) return .{ .existing = gop.existing };
8384
8385 const local = ip.getLocal(tid);
8386 const items = local.getMutableItems(gpa, io);
8387 const extra = local.getMutableExtra(gpa, io);
8388 try items.ensureUnusedCapacity(1);
8389
8390 const is_extern = switch (ini.layout) {
8391 .auto => false,
8392 .@"extern" => true,
8393 .@"packed" => {
8394 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnionPacked).@"struct".field_names.len +
8395 ini.captures.len + // capture
8396 ini.fields_len); // field_type
8397
8398 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{
8399 .zir_index = ini.zir_index,
8400 .bits = .{
8401 .captures_len = @fromBackingInt(@intCast(ini.captures.len)),
8402 .want_layout = false,
8403 },
8404 .name = undefined, // set by `finish`
8405 .fqn = undefined, // set by `finish`
8406 .name_nav = undefined, // set by `finish`
8407 .namespace = undefined, // set by `finish`
8408 .backing_int_type = .none,
8409 .enum_tag_type = .none,
8410 .fields_len = ini.fields_len,
8411 });
8412 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture
8413 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8414 items.appendAssumeCapacity(.{
8415 .tag = switch (ini.packed_backing_mode) {
8416 .auto => .type_union_packed_auto,
8417 .explicit => .type_union_packed_explicit,
8418 },
8419 .data = extra_index,
8420 });
8421 return .{ .wip = .{
8422 .index = gop.put(),
8423 .tid = tid,
8424 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?,
8425 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "fqn").?,
8426 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?,
8427 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?,
8428 .field_names = undefined,
8429 .field_types = undefined,
8430 .field_values = undefined,
8431 .field_aligns = undefined,
8432 .field_is_comptime_bits = undefined,
8433 } };
8434 },
8435 };
8436
8437 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".field_names.len +
8438 1 + // captures_len
8439 ini.captures.len + // capture
8440 ini.fields_len + // field_type
8441 (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0)); // field_align
8442
8443 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{
8444 .zir_index = ini.zir_index,
8445 .name = undefined, // set by `finish`
8446 .fqn = undefined, // set by `finish`
8447 .name_nav = undefined, // set by `finish`
8448 .namespace = undefined, // set by `finish`
8449 .enum_tag_type = .none,
8450 .fields_len = ini.fields_len,
8451 .size = 0,
8452 .padding = 0,
8453 .flags = .{
8454 .any_captures = if (ini.captures.len != 0) .true else .false,
8455 .enum_tag_mode = ini.enum_tag_mode,
8456 .layout = if (is_extern) .@"extern" else .auto,
8457 .any_field_aligns = ini.any_field_aligns,
8458 .tag_usage = ini.tag_usage,
8459 .class = .no_possible_value,
8460 .has_runtime_tag = false,
8461 .alignment = .none,
8462 .want_layout = false,
8463 },
8464 });
8465 if (ini.captures.len > 0) {
8466 extra.appendAssumeCapacity(.{@intCast(ini.captures.len)}); // captures_len
8467 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture
8468 }
8469 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8470 if (ini.any_field_aligns) {
8471 extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align
8472 }
8473 items.appendAssumeCapacity(.{
8474 .tag = .type_union,
8475 .data = extra_index,
8476 });
8477 return .{ .wip = .{
8478 .index = gop.put(),
8479 .tid = tid,
8480 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?,
8481 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "fqn").?,
8482 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?,
8483 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?,
8484 .field_names = undefined,
8485 .field_types = undefined,
8486 .field_values = undefined,
8487 .field_aligns = undefined,
8488 .field_is_comptime_bits = undefined,
8489 } };
8490}
8491
8492pub fn getReifiedUnionType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
8493 zir_index: TrackedInst.Index,
8494 type_hash: u64,
8495 fields_len: u32,
8496 layout: std.lang.Type.ContainerLayout,
8497 any_field_aligns: bool,
8498 tag_usage: LoadedUnionType.TagUsage,
8499 /// Explicitly specified enum tag type. `.none` if `tag_usage != .tagged`.
8500 enum_tag_type: Index,
8501 /// Explicitly specified backing int type. `.none` if not packed or if backing type is inferred.
8502 packed_backing_int_type: Index,
8503}) Allocator.Error!WipContainerType.Result {
8504 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .union_type = .{ .reified = .{
8505 .zir_index = ini.zir_index,
8506 .type_hash = ini.type_hash,
8507 } } });
8508 defer gop.deinit();
8509 if (gop == .existing) return .{ .existing = gop.existing };
8510
8511 const local = ip.getLocal(tid);
8512 const items = local.getMutableItems(gpa, io);
8513 const extra = local.getMutableExtra(gpa, io);
8514 try items.ensureUnusedCapacity(1);
8515
8516 const is_extern = switch (ini.layout) {
8517 .auto => false,
8518 .@"extern" => true,
8519 .@"packed" => {
8520 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnionPacked).@"struct".field_names.len +
8521 2 + // type_hash
8522 ini.fields_len + // reified_field_name
8523 ini.fields_len); // field_type
8524
8525 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnionPacked{
8526 .zir_index = ini.zir_index,
8527 .bits = .{
8528 .captures_len = .reified,
8529 .want_layout = false,
8530 },
8531 .name = undefined, // set by `finish`
8532 .fqn = undefined, // set by `finish`
8533 .name_nav = undefined, // set by `finish`
8534 .namespace = undefined, // set by `finish`
8535 .backing_int_type = ini.packed_backing_int_type,
8536 .enum_tag_type = .none,
8537 .fields_len = ini.fields_len,
8538 });
8539 _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash
8540 const field_names_start = extra.mutate.len;
8541 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // reified_field_name
8542 const field_types_start = extra.mutate.len;
8543 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8544 items.appendAssumeCapacity(.{
8545 .tag = switch (ini.packed_backing_int_type) {
8546 .none => .type_union_packed_auto,
8547 else => .type_union_packed_explicit,
8548 },
8549 .data = extra_index,
8550 });
8551 return .{ .wip = .{
8552 .index = gop.put(),
8553 .tid = tid,
8554 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name").?,
8555 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "fqn").?,
8556 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "name_nav").?,
8557 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnionPacked, "namespace").?,
8558 .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len },
8559 .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len },
8560 .field_values = undefined,
8561 .field_aligns = undefined,
8562 .field_is_comptime_bits = undefined,
8563 } };
8564 },
8565 };
8566
8567 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeUnion).@"struct".field_names.len +
8568 2 + // type_hash
8569 ini.fields_len + // reified_field_name
8570 ini.fields_len + // field_type
8571 (if (ini.any_field_aligns) (ini.fields_len + 3) / 4 else 0)); // field_align
8572
8573 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{
8574 .zir_index = ini.zir_index,
8575 .name = undefined, // set by `finish`
8576 .fqn = undefined, // set by `finish`
8577 .name_nav = undefined, // set by `finish`
8578 .namespace = undefined, // set by `finish`
8579 .enum_tag_type = ini.enum_tag_type,
8580 .fields_len = ini.fields_len,
8581 .size = 0,
8582 .padding = 0,
8583 .flags = .{
8584 .any_captures = .reified,
8585 .enum_tag_mode = if (ini.enum_tag_type == .none) .auto else .explicit,
8586 .layout = if (is_extern) .@"extern" else .auto,
8587 .any_field_aligns = ini.any_field_aligns,
8588 .tag_usage = ini.tag_usage,
8589 .class = .no_possible_value,
8590 .has_runtime_tag = false,
8591 .alignment = .none,
8592 .want_layout = false,
8593 },
8594 });
8595 _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash));
8596 const field_names_start = extra.mutate.len;
8597 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // reified_field_name
8598 const field_types_start = extra.mutate.len;
8599 extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_type
8600 const field_aligns_start = extra.mutate.len;
8601 if (ini.any_field_aligns) {
8602 extra.appendNTimesAssumeCapacity(.{0}, (ini.fields_len + 3) / 4); // field_align
8603 }
8604 items.appendAssumeCapacity(.{
8605 .tag = .type_union,
8606 .data = extra_index,
8607 });
8608 return .{ .wip = .{
8609 .index = gop.put(),
8610 .tid = tid,
8611 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?,
8612 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "fqn").?,
8613 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name_nav").?,
8614 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?,
8615 .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len },
8616 .field_types = .{ .tid = tid, .start = field_types_start, .len = ini.fields_len },
8617 .field_values = undefined,
8618 .field_aligns = if (ini.any_field_aligns)
8619 .{ .tid = tid, .start = field_aligns_start, .len = ini.fields_len }
8620 else
8621 undefined,
8622 .field_is_comptime_bits = undefined,
8623 } };
8624}
8625
8626pub fn getDeclaredEnumType(
8627 ip: *InternPool,
8628 gpa: Allocator,
8629 io: Io,
8630 tid: Zcu.PerThread.Id,
8631 ini: struct {
8632 zir_index: TrackedInst.Index,
8633 captures: []const CaptureValue,
8634
8635 // If the value of any of the following fields would change on an incremental update, then logic
8636 // in `Zcu.mapOldZirToNew` must detect that (these properties are all trivially known from ZIR)
8637 // and refuse to map the type declaration. This causes `zir_index` to change so that a new type
8638 // will be interned at a fresh index.
8639 //
8640 // In the future, it would be good to remove all of those fields from `ini`, and in fact just
8641 // have a single function `getDeclaredContainer` which is suitable for all container types.
8642 // However, this requires some major changes to how container types are represented in the
8643 // InternPool, so that it is possible for their backing storage to be "reallocated" as needed
8644 // during type resolution.
8645 fields_len: u32,
8646 nonexhaustive: bool,
8647 /// For `enum(T)` this is `.explicit`. Otherwise this is `.none`.
8648 int_tag_mode: BackingTypeMode,
8649 },
8650) Allocator.Error!WipContainerType.Result {
8651 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ .declared = .{
8652 .zir_index = ini.zir_index,
8653 .captures = .{ .external = ini.captures },
8654 } } });
8655 defer gop.deinit();
8656 if (gop == .existing) return .{ .existing = gop.existing };
8657
8658 const local = ip.getLocal(tid);
8659 const items = local.getMutableItems(gpa, io);
8660 const extra = local.getMutableExtra(gpa, io);
8661 try items.ensureUnusedCapacity(1);
8662
8663 const tag: Tag, const have_values: bool = if (ini.nonexhaustive)
8664 .{ .type_enum_nonexhaustive, true }
8665 else if (ini.int_tag_mode == .explicit)
8666 .{ .type_enum_explicit, true }
8667 else
8668 .{ .type_enum_auto, false };
8669
8670 const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
8671 errdefer local.mutate.maps.len -= 1;
8672
8673 const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined;
8674 errdefer local.mutate.maps.len -= @intFromBool(have_values);
8675
8676 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".field_names.len +
8677 1 + // zir_index
8678 ini.captures.len + // capture
8679 @intFromBool(have_values) + // field_value_map
8680 ini.fields_len + // field_name
8681 (if (have_values) ini.fields_len else 0)); // field_value
8682
8683 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{
8684 .bits = .{
8685 .captures_len = @fromBackingInt(@intCast(ini.captures.len)),
8686 .want_layout = false,
8687 },
8688 .name = undefined, // set by `finish`
8689 .fqn = undefined, // set by `finish`
8690 .name_nav = undefined, // set by `finish`
8691 .namespace = undefined, // set by `finish`
8692 .int_tag_type = .none,
8693 .fields_len = ini.fields_len,
8694 .field_name_map = field_name_map,
8695 });
8696 extra.appendAssumeCapacity(.{@backingInt(ini.zir_index)}); // zir_index
8697 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)}); // capture
8698 if (have_values) extra.appendAssumeCapacity(.{@backingInt(field_value_map)}); // field_value_map
8699 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8700 if (have_values) extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_value
8701 items.appendAssumeCapacity(.{
8702 .tag = tag,
8703 .data = extra_index,
8704 });
8705 return .{ .wip = .{
8706 .index = gop.put(),
8707 .tid = tid,
8708 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?,
8709 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "fqn").?,
8710 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?,
8711 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?,
8712 .field_names = undefined,
8713 .field_types = undefined,
8714 .field_values = undefined,
8715 .field_aligns = undefined,
8716 .field_is_comptime_bits = undefined,
8717 } };
8718}
8719
8720pub fn getReifiedEnumType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
8721 zir_index: TrackedInst.Index,
8722 type_hash: u64,
8723 fields_len: u32,
8724 nonexhaustive: bool,
8725 /// Explicitly specified int tag type, or `.none` if the int tag type is inferred.
8726 int_tag_type: Index,
8727}) Allocator.Error!WipContainerType.Result {
8728 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ .reified = .{
8729 .zir_index = ini.zir_index,
8730 .type_hash = ini.type_hash,
8731 } } });
8732 defer gop.deinit();
8733 if (gop == .existing) return .{ .existing = gop.existing };
8734
8735 const local = ip.getLocal(tid);
8736 const items = local.getMutableItems(gpa, io);
8737 const extra = local.getMutableExtra(gpa, io);
8738 try items.ensureUnusedCapacity(1);
8739
8740 const tag: Tag, const have_values: bool = if (ini.nonexhaustive)
8741 .{ .type_enum_nonexhaustive, true }
8742 else if (ini.int_tag_type != .none)
8743 .{ .type_enum_explicit, true }
8744 else
8745 .{ .type_enum_auto, false };
8746
8747 const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
8748 errdefer local.mutate.maps.len -= 1;
8749
8750 const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined;
8751 errdefer local.mutate.maps.len -= @intFromBool(have_values);
8752
8753 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".field_names.len +
8754 1 + // zir_index
8755 2 + // type_hash
8756 @intFromBool(have_values) + // field_value_map
8757 ini.fields_len + // field_name
8758 (if (have_values) ini.fields_len else 0)); // field_value
8759
8760 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{
8761 .bits = .{
8762 .captures_len = .reified,
8763 .want_layout = false,
8764 },
8765 .name = undefined, // set by `finish`
8766 .fqn = undefined, // set by `finish`
8767 .name_nav = undefined, // set by `finish`
8768 .namespace = undefined, // set by `finish`
8769 .int_tag_type = ini.int_tag_type,
8770 .fields_len = ini.fields_len,
8771 .field_name_map = field_name_map,
8772 });
8773 extra.appendAssumeCapacity(.{@backingInt(ini.zir_index)}); // zir_index
8774 _ = addExtraAssumeCapacity(extra, PackedU64.init(ini.type_hash)); // type_hash
8775 if (have_values) extra.appendAssumeCapacity(.{@backingInt(field_value_map)}); // field_value_map
8776 const field_names_start = extra.mutate.len;
8777 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8778 const field_values_start = extra.mutate.len;
8779 if (have_values) extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_value
8780 items.appendAssumeCapacity(.{
8781 .tag = tag,
8782 .data = extra_index,
8783 });
8784 return .{ .wip = .{
8785 .index = gop.put(),
8786 .tid = tid,
8787 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?,
8788 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "fqn").?,
8789 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?,
8790 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?,
8791 .field_names = .{ .tid = tid, .start = field_names_start, .len = ini.fields_len },
8792 .field_types = undefined,
8793 .field_values = if (have_values)
8794 .{ .tid = tid, .start = field_values_start, .len = ini.fields_len }
8795 else
8796 undefined,
8797 .field_aligns = undefined,
8798 .field_is_comptime_bits = undefined,
8799 } };
8800}
8801
8802pub fn getReifiedSpirvType(
8803 ip: *InternPool,
8804 gpa: Allocator,
8805 io: Io,
8806 tid: Zcu.PerThread.Id,
8807 type_spirv: Key.SpirvType,
8808) Allocator.Error!Index {
8809 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .spirv_type = .{
8810 .ty = type_spirv.ty,
8811 .flags = type_spirv.flags,
8812 } });
8813 defer gop.deinit();
8814 if (gop == .existing) return gop.existing;
8815
8816 const local = ip.getLocal(tid);
8817 const items = local.getMutableItems(gpa, io);
8818 const extra = local.getMutableExtra(gpa, io);
8819 try items.ensureUnusedCapacity(1);
8820
8821 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeSpirv).@"struct".field_names.len);
8822 const extra_index = addExtraAssumeCapacity(extra, type_spirv);
8823
8824 items.appendAssumeCapacity(.{ .tag = .type_spirv, .data = extra_index });
8825 return gop.put();
8826}
8827
8828pub fn getGeneratedEnumTagType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
8829 /// The union type for which this enum is a generated tag.
8830 union_type: Index,
8831 /// For `union(enum(T))` this is `.explicit`. Otherwise this is `.none`.
8832 int_tag_mode: BackingTypeMode,
8833 fields_len: u32,
8834}) Allocator.Error!WipContainerType.Result {
8835 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .enum_type = .{ .generated_union_tag = ini.union_type } });
8836 defer gop.deinit();
8837 if (gop == .existing) return .{ .existing = gop.existing };
8838
8839 const local = ip.getLocal(tid);
8840 const items = local.getMutableItems(gpa, io);
8841 const extra = local.getMutableExtra(gpa, io);
8842 try items.ensureUnusedCapacity(1);
8843
8844 const field_name_map = try ip.addMap(gpa, io, tid, ini.fields_len);
8845 errdefer local.mutate.maps.len -= 1;
8846
8847 const have_values = switch (ini.int_tag_mode) {
8848 .explicit => true,
8849 .auto => false,
8850 };
8851
8852 const field_value_map = if (have_values) try ip.addMap(gpa, io, tid, ini.fields_len) else undefined;
8853 errdefer local.mutate.maps.len -= @intFromBool(have_values);
8854
8855 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeEnum).@"struct".field_names.len +
8856 1 + // owner_union
8857 @intFromBool(have_values) + // field_value_map
8858 ini.fields_len + // field_name
8859 (if (have_values) ini.fields_len else 0)); // field_value
8860
8861 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeEnum{
8862 .bits = .{
8863 .captures_len = .generated_union_tag,
8864 .want_layout = false,
8865 },
8866 .name = undefined, // set by `finish`
8867 .fqn = undefined, // set by `finish`
8868 .name_nav = undefined, // set by `finish`
8869 .namespace = undefined, // set by `finish`
8870 .int_tag_type = .none,
8871 .fields_len = ini.fields_len,
8872 .field_name_map = field_name_map,
8873 });
8874 extra.appendAssumeCapacity(.{@backingInt(ini.union_type)}); // owner_union
8875 if (have_values) extra.appendAssumeCapacity(.{@backingInt(field_value_map)});
8876 extra.appendNTimesAssumeCapacity(.{@backingInt(NullTerminatedString.empty)}, ini.fields_len); // field_name
8877 if (have_values) extra.appendNTimesAssumeCapacity(.{@backingInt(Index.none)}, ini.fields_len); // field_value
8878 items.appendAssumeCapacity(.{
8879 .tag = switch (ini.int_tag_mode) {
8880 .auto => .type_enum_auto,
8881 .explicit => .type_enum_explicit,
8882 },
8883 .data = extra_index,
8884 });
8885 return .{ .wip = .{
8886 .index = gop.put(),
8887 .tid = tid,
8888 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name").?,
8889 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "fqn").?,
8890 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "name_nav").?,
8891 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeEnum, "namespace").?,
8892 .field_names = undefined,
8893 .field_types = undefined,
8894 .field_values = undefined,
8895 .field_aligns = undefined,
8896 .field_is_comptime_bits = undefined,
8897 } };
8898}
8899
8900pub fn getDeclaredOpaqueType(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, ini: struct {
8901 zir_index: TrackedInst.Index,
8902 captures: []const CaptureValue,
8903}) Allocator.Error!WipContainerType.Result {
8904 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .opaque_type = .{ .declared = .{
8905 .zir_index = ini.zir_index,
8906 .captures = .{ .external = ini.captures },
8907 } } });
8908 defer gop.deinit();
8909 if (gop == .existing) return .{ .existing = gop.existing };
8910
8911 const local = ip.getLocal(tid);
8912 const items = local.getMutableItems(gpa, io);
8913 const extra = local.getMutableExtra(gpa, io);
8914 try items.ensureUnusedCapacity(1);
8915
8916 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeOpaque).@"struct".field_names.len + ini.captures.len);
8917 const extra_index = addExtraAssumeCapacity(extra, Tag.TypeOpaque{
8918 .zir_index = ini.zir_index,
8919 .captures_len = @intCast(ini.captures.len),
8920 .name = undefined, // set by `finish`
8921 .fqn = undefined, // set by `finish`
8922 .name_nav = undefined, // set by `finish`
8923 .namespace = undefined, // set by `finish`
8924 });
8925 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.captures)});
8926 items.appendAssumeCapacity(.{
8927 .tag = .type_opaque,
8928 .data = extra_index,
8929 });
8930 return .{ .wip = .{
8931 .index = gop.put(),
8932 .tid = tid,
8933 .type_name_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?,
8934 .type_fqn_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "fqn").?,
8935 .name_nav_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name_nav").?,
8936 .namespace_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?,
8937 .field_names = undefined,
8938 .field_types = undefined,
8939 .field_values = undefined,
8940 .field_aligns = undefined,
8941 .field_is_comptime_bits = undefined,
8942 } };
8943}
8944
8945pub const WipContainerType = struct {
8946 index: Index,
8947 tid: Zcu.PerThread.Id,
8948 type_name_index: u32,
8949 type_fqn_index: u32,
8950 name_nav_index: u32,
8951 namespace_index: u32,
8952
8953 // These fields are only populated when creating reified types, because reified types populate
8954 // field information immediately, with type resolution only handling validation. This is in
8955 // contrast to declared types, where field information is populated by the type resolution
8956 // process evaluating ZIR expressions.
8957 field_names: NullTerminatedString.Slice,
8958 field_types: Index.Slice,
8959 field_values: Index.Slice,
8960 field_aligns: Alignment.Slice,
8961 field_is_comptime_bits: LoadedStructType.ComptimeBits,
8962
8963 pub fn setName(
8964 wip: WipContainerType,
8965 ip: *InternPool,
8966 type_name: NullTerminatedString,
8967 type_fqn: NullTerminatedString,
8968 /// This should be the `Nav` we are named after if we use the `.parent` name strategy; `.none` otherwise.
8969 /// This is also `.none` if we use `.parent` because we are the root struct type for a file.
8970 name_nav: Nav.Index.Optional,
8971 ) void {
8972 const extra = ip.getLocalShared(wip.tid).extra.acquire();
8973 const extra_items = extra.view().items(.@"0");
8974 extra_items[wip.type_name_index] = @backingInt(type_name);
8975 extra_items[wip.type_fqn_index] = @backingInt(type_fqn);
8976 extra_items[wip.name_nav_index] = @backingInt(name_nav);
8977 }
8978
8979 pub fn finish(
8980 wip: WipContainerType,
8981 ip: *InternPool,
8982 namespace: NamespaceIndex,
8983 ) Index {
8984 const extra = ip.getLocalShared(wip.tid).extra.acquire();
8985 const extra_items = extra.view().items(.@"0");
8986
8987 extra_items[wip.namespace_index] = @backingInt(namespace);
8988
8989 return wip.index;
8990 }
8991
8992 pub fn cancel(wip: WipContainerType, ip: *InternPool, tid: Zcu.PerThread.Id) void {
8993 ip.remove(tid, wip.index);
8994 }
8995
8996 pub const Result = union(enum) {
8997 wip: WipContainerType,
8998 existing: Index,
8999 };
9000};
9001
9002pub fn getUnion(
9003 ip: *InternPool,
9004 gpa: Allocator,
9005 io: Io,
9006 tid: Zcu.PerThread.Id,
9007 un: Key.Union,
9008) Allocator.Error!Index {
9009 assert(un.ty != .none);
9010 assert(un.val != .none);
9011 assert(ip.loadUnionType(un.ty).layout != .@"packed");
9012
9013 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
9014 defer gop.deinit();
9015 if (gop == .existing) return gop.existing;
9016 const local = ip.getLocal(tid);
9017 const items = local.getMutableItems(gpa, io);
9018 const extra = local.getMutableExtra(gpa, io);
9019 try items.ensureUnusedCapacity(1);
9020
9021 items.appendAssumeCapacity(.{
9022 .tag = .union_value,
9023 .data = try addExtra(extra, un),
9024 });
9025
9026 return gop.put();
9027}
9028
9029pub const TupleTypeInit = struct {
9030 types: []const Index,
9031 /// These elements may be `none`, indicating runtime-known.
9032 values: []const Index,
9033};
9034
9035pub fn getTupleType(
9036 ip: *InternPool,
9037 gpa: Allocator,
9038 io: Io,
9039 tid: Zcu.PerThread.Id,
9040 ini: TupleTypeInit,
9041) Allocator.Error!Index {
9042 assert(ini.types.len == ini.values.len);
9043 for (ini.types) |elem| assert(elem != .none);
9044
9045 const local = ip.getLocal(tid);
9046 const items = local.getMutableItems(gpa, io);
9047 const extra = local.getMutableExtra(gpa, io);
9048
9049 const prev_extra_len = extra.mutate.len;
9050 const fields_len: u32 = @intCast(ini.types.len);
9051
9052 try items.ensureUnusedCapacity(1);
9053 try extra.ensureUnusedCapacity(
9054 @typeInfo(TypeTuple).@"struct".field_names.len + (fields_len * 3),
9055 );
9056
9057 const extra_index = addExtraAssumeCapacity(extra, TypeTuple{
9058 .fields_len = fields_len,
9059 });
9060 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.types)});
9061 extra.appendSliceAssumeCapacity(.{@ptrCast(ini.values)});
9062 errdefer extra.mutate.len = prev_extra_len;
9063
9064 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .tuple_type = extraTypeTuple(tid, extra.list.*, extra_index) });
9065 defer gop.deinit();
9066 if (gop == .existing) {
9067 extra.mutate.len = prev_extra_len;
9068 return gop.existing;
9069 }
9070
9071 items.appendAssumeCapacity(.{
9072 .tag = .type_tuple,
9073 .data = extra_index,
9074 });
9075 return gop.put();
9076}
9077
9078/// This is equivalent to `Key.FuncType` but adjusted to have a slice for `param_types`.
9079pub const GetFuncTypeKey = struct {
9080 param_types: []const Index,
9081 return_type: Index,
9082 comptime_bits: u32 = 0,
9083 noalias_bits: u32 = 0,
9084 /// `null` means generic.
9085 cc: ?std.lang.CallingConvention = .auto,
9086 is_var_args: bool = false,
9087 is_noinline: bool = false,
9088};
9089
9090pub fn getFuncType(
9091 ip: *InternPool,
9092 gpa: Allocator,
9093 io: Io,
9094 tid: Zcu.PerThread.Id,
9095 key: GetFuncTypeKey,
9096) Allocator.Error!Index {
9097 // Validate input parameters.
9098 assert(key.return_type != .none);
9099 for (key.param_types) |param_type| assert(param_type != .none);
9100
9101 const local = ip.getLocal(tid);
9102 const items = local.getMutableItems(gpa, io);
9103 try items.ensureUnusedCapacity(1);
9104 const extra = local.getMutableExtra(gpa, io);
9105
9106 // The strategy here is to add the function type unconditionally, then to
9107 // ask if it already exists, and if so, revert the lengths of the mutated
9108 // arrays. This is similar to what `getOrPutTrailingString` does.
9109 const prev_extra_len = extra.mutate.len;
9110 const packed_cc: PackedCallingConvention = .pack(key.cc orelse .auto);
9111 const cc_extra_len = packed_cc.extraLen();
9112 const params_len: u32 = @intCast(key.param_types.len);
9113
9114 try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeFunction).@"struct".field_names.len +
9115 @intFromBool(key.comptime_bits != 0) +
9116 @intFromBool(key.noalias_bits != 0) +
9117 cc_extra_len +
9118 params_len);
9119
9120 const func_type_extra_index = addExtraAssumeCapacity(extra, Tag.TypeFunction{
9121 .params_len = params_len,
9122 .return_type = key.return_type,
9123 .flags = .{
9124 .cc = packed_cc,
9125 .is_var_args = key.is_var_args,
9126 .has_comptime_bits = key.comptime_bits != 0,
9127 .has_noalias_bits = key.noalias_bits != 0,
9128 .is_noinline = key.is_noinline,
9129 },
9130 });
9131
9132 if (key.comptime_bits != 0) extra.appendAssumeCapacity(.{key.comptime_bits});
9133 if (key.noalias_bits != 0) extra.appendAssumeCapacity(.{key.noalias_bits});
9134 if (key.cc) |cc| switch (cc) {
9135 .spirv_kernel, .spirv_task => |kernel| extra.appendSliceAssumeCapacity(.{&.{
9136 kernel.x,
9137 kernel.y,
9138 kernel.z,
9139 }}),
9140 .spirv_mesh => |mesh| extra.appendSliceAssumeCapacity(.{&.{
9141 mesh.max_primitives,
9142 mesh.max_vertices,
9143 mesh.x,
9144 mesh.y,
9145 mesh.z,
9146 }}),
9147 else => {},
9148 };
9149 extra.appendSliceAssumeCapacity(.{@ptrCast(key.param_types)});
9150 errdefer extra.mutate.len = prev_extra_len;
9151
9152 var gop = try ip.getOrPutKey(gpa, io, tid, .{
9153 .func_type = extraFuncType(tid, extra.list.*, func_type_extra_index),
9154 });
9155 defer gop.deinit();
9156 if (gop == .existing) {
9157 extra.mutate.len = prev_extra_len;
9158 return gop.existing;
9159 }
9160
9161 items.appendAssumeCapacity(.{
9162 .tag = .type_function,
9163 .data = func_type_extra_index,
9164 });
9165 return gop.put();
9166}
9167
9168/// Intern an `.@"extern"`, creating a corresponding owner `Nav` if necessary.
9169/// This will *not* queue the extern for codegen: see `Zcu.PerThread.getExtern` for a wrapper which does.
9170pub fn getExtern(
9171 ip: *InternPool,
9172 gpa: Allocator,
9173 io: Io,
9174 tid: Zcu.PerThread.Id,
9175 /// `key.owner_nav` is ignored.
9176 key: Key.Extern,
9177) Allocator.Error!struct {
9178 index: Index,
9179 /// Only set if the `Nav` was newly created.
9180 new_nav: Nav.Index.Optional,
9181} {
9182 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .@"extern" = key });
9183 defer gop.deinit();
9184 if (gop == .existing) return .{
9185 .index = gop.existing,
9186 .new_nav = .none,
9187 };
9188
9189 const local = ip.getLocal(tid);
9190 const items = local.getMutableItems(gpa, io);
9191 const extra = local.getMutableExtra(gpa, io);
9192 try items.ensureUnusedCapacity(1);
9193 try extra.ensureUnusedCapacity(@typeInfo(Tag.Extern).@"struct".field_names.len);
9194 try local.getMutableNavs(gpa, io).ensureUnusedCapacity(1);
9195
9196 // Predict the index the `@"extern" will live at, so we can construct the owner `Nav` before releasing the shard's mutex.
9197 const extern_index = Index.Unwrapped.wrap(.{
9198 .tid = tid,
9199 .index = items.mutate.len,
9200 }, ip);
9201 const owner_nav = ip.createNav(gpa, io, tid, key.name, key.name, .{
9202 .type = key.ty,
9203 .@"align" = key.alignment,
9204 .@"linksection" = .none,
9205 .@"addrspace" = key.@"addrspace",
9206 .@"const" = key.is_const,
9207 .@"threadlocal" = key.is_threadlocal,
9208 .is_extern_decl = true,
9209 .value = extern_index,
9210 }) catch unreachable; // capacity asserted above
9211 const decoration_type, const location_or_descriptor_set, const descriptor_binding = if (key.decoration) |decoration| switch (decoration) {
9212 .location => |location| .{ Tag.Extern.Flags.DecorationType.location, location, undefined },
9213 .flat => |location| .{ Tag.Extern.Flags.DecorationType.flat, location, undefined },
9214 .descriptor => |descriptor| .{ Tag.Extern.Flags.DecorationType.descriptor, descriptor.set, descriptor.binding },
9215 } else .{ Tag.Extern.Flags.DecorationType.none, undefined, undefined };
9216 const extra_index = addExtraAssumeCapacity(extra, Tag.Extern{
9217 .ty = key.ty,
9218 .lib_name = key.lib_name,
9219 .location_or_descriptor_set = location_or_descriptor_set,
9220 .descriptor_binding = descriptor_binding,
9221 .flags = .{
9222 .linkage = key.linkage,
9223 .visibility = key.visibility,
9224 .is_dll_import = key.is_dll_import,
9225 .relocation = key.relocation,
9226 .decoration_type = decoration_type,
9227 .source = key.source,
9228 },
9229 .zir_index = key.zir_index,
9230 .owner_nav = owner_nav,
9231 });
9232 items.appendAssumeCapacity(.{
9233 .tag = .@"extern",
9234 .data = extra_index,
9235 });
9236 assert(gop.put() == extern_index);
9237
9238 return .{
9239 .index = extern_index,
9240 .new_nav = owner_nav.toOptional(),
9241 };
9242}
9243
9244pub const GetFuncDeclKey = struct {
9245 owner_nav: Nav.Index,
9246 ty: Index,
9247 zir_body_inst: TrackedInst.Index,
9248 lbrace_line: u32,
9249 rbrace_line: u32,
9250 lbrace_column: u32,
9251 rbrace_column: u32,
9252 cc: ?std.lang.CallingConvention,
9253 is_noinline: bool,
9254};
9255
9256pub fn getFuncDecl(
9257 ip: *InternPool,
9258 gpa: Allocator,
9259 io: Io,
9260 tid: Zcu.PerThread.Id,
9261 key: GetFuncDeclKey,
9262) Allocator.Error!Index {
9263 const local = ip.getLocal(tid);
9264 const items = local.getMutableItems(gpa, io);
9265 try items.ensureUnusedCapacity(1);
9266 const extra = local.getMutableExtra(gpa, io);
9267
9268 // The strategy here is to add the function type unconditionally, then to
9269 // ask if it already exists, and if so, revert the lengths of the mutated
9270 // arrays. This is similar to what `getOrPutTrailingString` does.
9271 const prev_extra_len = extra.mutate.len;
9272
9273 try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncDecl).@"struct".field_names.len);
9274
9275 const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
9276 .analysis = .{
9277 .want_runtime_analysis = false,
9278 .branch_hint = .none,
9279 .is_noinline = key.is_noinline,
9280 .has_error_trace = false,
9281 .inferred_error_set = false,
9282 .disable_instrumentation = false,
9283 .disable_intrinsics = false,
9284 },
9285 .owner_nav = key.owner_nav,
9286 .ty = key.ty,
9287 .zir_body_inst = key.zir_body_inst,
9288 .lbrace_line = key.lbrace_line,
9289 .rbrace_line = key.rbrace_line,
9290 .lbrace_column = key.lbrace_column,
9291 .rbrace_column = key.rbrace_column,
9292 });
9293 errdefer extra.mutate.len = prev_extra_len;
9294
9295 var gop = try ip.getOrPutKey(gpa, io, tid, .{
9296 .func = extraFuncDecl(tid, extra.list.*, func_decl_extra_index),
9297 });
9298 defer gop.deinit();
9299 if (gop == .existing) {
9300 extra.mutate.len = prev_extra_len;
9301
9302 const zir_body_inst_ptr = ip.funcDeclInfo(gop.existing).zirBodyInstPtr(ip);
9303 if (zir_body_inst_ptr.* != key.zir_body_inst) {
9304 // Since this function's `owner_nav` matches `key`, this *is* the function we're talking
9305 // about. The only way it could have a different ZIR `func` instruction is if the old
9306 // instruction has been lost and replaced with a new `TrackedInst.Index`.
9307 assert(zir_body_inst_ptr.resolve(ip) == null);
9308 zir_body_inst_ptr.* = key.zir_body_inst;
9309 }
9310
9311 return gop.existing;
9312 }
9313
9314 items.appendAssumeCapacity(.{
9315 .tag = .func_decl,
9316 .data = func_decl_extra_index,
9317 });
9318 return gop.put();
9319}
9320
9321pub const GetFuncDeclIesKey = struct {
9322 owner_nav: Nav.Index,
9323 param_types: []Index,
9324 noalias_bits: u32,
9325 comptime_bits: u32,
9326 bare_return_type: Index,
9327 /// null means generic.
9328 cc: ?std.lang.CallingConvention,
9329 is_var_args: bool,
9330 is_noinline: bool,
9331 zir_body_inst: TrackedInst.Index,
9332 lbrace_line: u32,
9333 rbrace_line: u32,
9334 lbrace_column: u32,
9335 rbrace_column: u32,
9336};
9337
9338pub fn getFuncDeclIes(
9339 ip: *InternPool,
9340 gpa: Allocator,
9341 io: Io,
9342 tid: Zcu.PerThread.Id,
9343 key: GetFuncDeclIesKey,
9344) Allocator.Error!Index {
9345 // Validate input parameters.
9346 assert(key.bare_return_type != .none);
9347 for (key.param_types) |param_type| assert(param_type != .none);
9348
9349 const local = ip.getLocal(tid);
9350 const items = local.getMutableItems(gpa, io);
9351 try items.ensureUnusedCapacity(4);
9352 const extra = local.getMutableExtra(gpa, io);
9353
9354 // The strategy here is to add the function decl unconditionally, then to
9355 // ask if it already exists, and if so, revert the lengths of the mutated
9356 // arrays. This is similar to what `getOrPutTrailingString` does.
9357 const prev_extra_len = extra.mutate.len;
9358 const params_len: u32 = @intCast(key.param_types.len);
9359
9360 try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncDecl).@"struct".field_names.len +
9361 1 + // inferred_error_set
9362 @typeInfo(Tag.ErrorUnionType).@"struct".field_names.len +
9363 @typeInfo(Tag.TypeFunction).@"struct".field_names.len +
9364 @intFromBool(key.comptime_bits != 0) +
9365 @intFromBool(key.noalias_bits != 0) +
9366 params_len);
9367
9368 const func_index = Index.Unwrapped.wrap(.{
9369 .tid = tid,
9370 .index = items.mutate.len + 0,
9371 }, ip);
9372 const error_union_type = Index.Unwrapped.wrap(.{
9373 .tid = tid,
9374 .index = items.mutate.len + 1,
9375 }, ip);
9376 const error_set_type = Index.Unwrapped.wrap(.{
9377 .tid = tid,
9378 .index = items.mutate.len + 2,
9379 }, ip);
9380 const func_ty = Index.Unwrapped.wrap(.{
9381 .tid = tid,
9382 .index = items.mutate.len + 3,
9383 }, ip);
9384
9385 const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
9386 .analysis = .{
9387 .want_runtime_analysis = false,
9388 .branch_hint = .none,
9389 .is_noinline = key.is_noinline,
9390 .has_error_trace = false,
9391 .inferred_error_set = true,
9392 .disable_instrumentation = false,
9393 .disable_intrinsics = false,
9394 },
9395 .owner_nav = key.owner_nav,
9396 .ty = func_ty,
9397 .zir_body_inst = key.zir_body_inst,
9398 .lbrace_line = key.lbrace_line,
9399 .rbrace_line = key.rbrace_line,
9400 .lbrace_column = key.lbrace_column,
9401 .rbrace_column = key.rbrace_column,
9402 });
9403 extra.appendAssumeCapacity(.{@backingInt(Index.none)});
9404
9405 const func_type_extra_index = addExtraAssumeCapacity(extra, Tag.TypeFunction{
9406 .params_len = params_len,
9407 .return_type = error_union_type,
9408 .flags = .{
9409 .cc = .pack(key.cc orelse .auto),
9410 .is_var_args = key.is_var_args,
9411 .has_comptime_bits = key.comptime_bits != 0,
9412 .has_noalias_bits = key.noalias_bits != 0,
9413 .is_noinline = key.is_noinline,
9414 },
9415 });
9416 if (key.comptime_bits != 0) extra.appendAssumeCapacity(.{key.comptime_bits});
9417 if (key.noalias_bits != 0) extra.appendAssumeCapacity(.{key.noalias_bits});
9418 extra.appendSliceAssumeCapacity(.{@ptrCast(key.param_types)});
9419
9420 items.appendSliceAssumeCapacity(.{
9421 .tag = &.{
9422 .func_decl,
9423 .type_error_union,
9424 .type_inferred_error_set,
9425 .type_function,
9426 },
9427 .data = &.{
9428 func_decl_extra_index,
9429 addExtraAssumeCapacity(extra, Tag.ErrorUnionType{
9430 .error_set_type = error_set_type,
9431 .payload_type = key.bare_return_type,
9432 }),
9433 @backingInt(func_index),
9434 func_type_extra_index,
9435 },
9436 });
9437 errdefer {
9438 items.mutate.len -= 4;
9439 extra.mutate.len = prev_extra_len;
9440 }
9441
9442 var func_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
9443 .func = extraFuncDecl(tid, extra.list.*, func_decl_extra_index),
9444 }, 3);
9445 defer func_gop.deinit();
9446 if (func_gop == .existing) {
9447 // An existing function type was found; undo the additions to our two arrays.
9448 items.mutate.len -= 4;
9449 extra.mutate.len = prev_extra_len;
9450
9451 const zir_body_inst_ptr = ip.funcDeclInfo(func_gop.existing).zirBodyInstPtr(ip);
9452 if (zir_body_inst_ptr.* != key.zir_body_inst) {
9453 // Since this function's `owner_nav` matches `key`, this *is* the function we're talking
9454 // about. The only way it could have a different ZIR `func` instruction is if the old
9455 // instruction has been lost and replaced with a new `TrackedInst.Index`.
9456 assert(zir_body_inst_ptr.resolve(ip) == null);
9457 zir_body_inst_ptr.* = key.zir_body_inst;
9458 }
9459
9460 return func_gop.existing;
9461 }
9462 func_gop.putTentative(func_index);
9463 var error_union_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{ .error_union_type = .{
9464 .error_set_type = error_set_type,
9465 .payload_type = key.bare_return_type,
9466 } }, 2);
9467 defer error_union_type_gop.deinit();
9468 error_union_type_gop.putTentative(error_union_type);
9469 var error_set_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
9470 .inferred_error_set_type = func_index,
9471 }, 1);
9472 defer error_set_type_gop.deinit();
9473 error_set_type_gop.putTentative(error_set_type);
9474 var func_ty_gop = try ip.getOrPutKey(gpa, io, tid, .{
9475 .func_type = extraFuncType(tid, extra.list.*, func_type_extra_index),
9476 });
9477 defer func_ty_gop.deinit();
9478 func_ty_gop.putTentative(func_ty);
9479
9480 func_gop.putFinal(func_index);
9481 error_union_type_gop.putFinal(error_union_type);
9482 error_set_type_gop.putFinal(error_set_type);
9483 func_ty_gop.putFinal(func_ty);
9484 return func_index;
9485}
9486
9487pub fn getErrorSetType(
9488 ip: *InternPool,
9489 gpa: Allocator,
9490 io: Io,
9491 tid: Zcu.PerThread.Id,
9492 names: []const NullTerminatedString,
9493) Allocator.Error!Index {
9494 assert(std.sort.isSorted(NullTerminatedString, names, {}, NullTerminatedString.indexLessThan));
9495
9496 const local = ip.getLocal(tid);
9497 const items = local.getMutableItems(gpa, io);
9498 const extra = local.getMutableExtra(gpa, io);
9499 try extra.ensureUnusedCapacity(@typeInfo(Tag.ErrorSet).@"struct".field_names.len + names.len);
9500
9501 const names_map = try ip.addMap(gpa, io, tid, names.len);
9502 errdefer local.mutate.maps.len -= 1;
9503
9504 // The strategy here is to add the type unconditionally, then to ask if it
9505 // already exists, and if so, revert the lengths of the mutated arrays.
9506 // This is similar to what `getOrPutTrailingString` does.
9507 const prev_extra_len = extra.mutate.len;
9508 errdefer extra.mutate.len = prev_extra_len;
9509
9510 const error_set_extra_index = addExtraAssumeCapacity(extra, Tag.ErrorSet{
9511 .names_len = @intCast(names.len),
9512 .names_map = names_map,
9513 });
9514 extra.appendSliceAssumeCapacity(.{@ptrCast(names)});
9515 errdefer extra.mutate.len = prev_extra_len;
9516
9517 var gop = try ip.getOrPutKey(gpa, io, tid, .{
9518 .error_set_type = extraErrorSet(tid, extra.list.*, error_set_extra_index),
9519 });
9520 defer gop.deinit();
9521 if (gop == .existing) {
9522 extra.mutate.len = prev_extra_len;
9523 return gop.existing;
9524 }
9525
9526 try items.append(.{
9527 .tag = .type_error_set,
9528 .data = error_set_extra_index,
9529 });
9530 errdefer items.mutate.len -= 1;
9531
9532 ip.addStringsToMap(names_map, names);
9533
9534 return gop.put();
9535}
9536
9537pub const GetFuncInstanceKey = struct {
9538 /// Has the length of the instance function (may be lesser than
9539 /// comptime_args).
9540 param_types: []Index,
9541 /// Has the length of generic_owner's parameters (may be greater than
9542 /// param_types).
9543 comptime_args: []const Index,
9544 noalias_bits: u32,
9545 bare_return_type: Index,
9546 is_noinline: bool,
9547 generic_owner: Index,
9548 inferred_error_set: bool,
9549 anon_name_counter: *u32,
9550};
9551
9552pub fn getFuncInstance(
9553 ip: *InternPool,
9554 gpa: Allocator,
9555 io: Io,
9556 tid: Zcu.PerThread.Id,
9557 arg: GetFuncInstanceKey,
9558) Allocator.Error!Index {
9559 if (arg.inferred_error_set)
9560 return getFuncInstanceIes(ip, gpa, io, tid, arg);
9561
9562 const generic_owner = unwrapCoercedFunc(ip, arg.generic_owner);
9563 const generic_owner_ty = ip.indexToKey(ip.funcDeclInfo(generic_owner).ty).func_type;
9564
9565 const func_ty = try ip.getFuncType(gpa, io, tid, .{
9566 .param_types = arg.param_types,
9567 .return_type = arg.bare_return_type,
9568 .noalias_bits = arg.noalias_bits,
9569 .cc = generic_owner_ty.cc,
9570 .is_noinline = arg.is_noinline,
9571 });
9572
9573 const local = ip.getLocal(tid);
9574 const items = local.getMutableItems(gpa, io);
9575 const extra = local.getMutableExtra(gpa, io);
9576 try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncInstance).@"struct".field_names.len +
9577 arg.comptime_args.len);
9578
9579 assert(arg.comptime_args.len == ip.funcTypeParamsLen(ip.typeOf(generic_owner)));
9580
9581 const prev_extra_len = extra.mutate.len;
9582 errdefer extra.mutate.len = prev_extra_len;
9583
9584 const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
9585 .analysis = .{
9586 .want_runtime_analysis = false,
9587 .branch_hint = .none,
9588 .is_noinline = arg.is_noinline,
9589 .has_error_trace = false,
9590 .inferred_error_set = false,
9591 .disable_instrumentation = false,
9592 .disable_intrinsics = false,
9593 },
9594 // This is populated after we create the Nav below. It is not read
9595 // by equality or hashing functions.
9596 .owner_nav = undefined,
9597 .ty = func_ty,
9598 .branch_quota = 0,
9599 .generic_owner = generic_owner,
9600 });
9601 extra.appendSliceAssumeCapacity(.{@ptrCast(arg.comptime_args)});
9602
9603 var gop = try ip.getOrPutKey(gpa, io, tid, .{
9604 .func = ip.extraFuncInstance(tid, extra.list.*, func_extra_index),
9605 });
9606 defer gop.deinit();
9607 if (gop == .existing) {
9608 extra.mutate.len = prev_extra_len;
9609 return gop.existing;
9610 }
9611
9612 const func_index = Index.Unwrapped.wrap(.{ .tid = tid, .index = items.mutate.len }, ip);
9613 try items.append(.{
9614 .tag = .func_instance,
9615 .data = func_extra_index,
9616 });
9617 errdefer items.mutate.len -= 1;
9618 try finishFuncInstance(
9619 ip,
9620 gpa,
9621 io,
9622 tid,
9623 extra,
9624 generic_owner,
9625 func_index,
9626 func_extra_index,
9627 arg.anon_name_counter,
9628 );
9629 return gop.put();
9630}
9631
9632/// This function exists separately than `getFuncInstance` because it needs to
9633/// create 4 new items in the InternPool atomically before it can look for an
9634/// existing item in the map.
9635fn getFuncInstanceIes(
9636 ip: *InternPool,
9637 gpa: Allocator,
9638 io: Io,
9639 tid: Zcu.PerThread.Id,
9640 arg: GetFuncInstanceKey,
9641) Allocator.Error!Index {
9642 // Validate input parameters.
9643 assert(arg.inferred_error_set);
9644 assert(arg.bare_return_type != .none);
9645 for (arg.param_types) |param_type| assert(param_type != .none);
9646
9647 const local = ip.getLocal(tid);
9648 const items = local.getMutableItems(gpa, io);
9649 const extra = local.getMutableExtra(gpa, io);
9650 try items.ensureUnusedCapacity(4);
9651
9652 const generic_owner = unwrapCoercedFunc(ip, arg.generic_owner);
9653 const generic_owner_ty = ip.indexToKey(ip.funcDeclInfo(generic_owner).ty).func_type;
9654
9655 // The strategy here is to add the function decl unconditionally, then to
9656 // ask if it already exists, and if so, revert the lengths of the mutated
9657 // arrays. This is similar to what `getOrPutTrailingString` does.
9658 const prev_extra_len = extra.mutate.len;
9659 const params_len: u32 = @intCast(arg.param_types.len);
9660
9661 try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncInstance).@"struct".field_names.len +
9662 1 + // inferred_error_set
9663 arg.comptime_args.len +
9664 @typeInfo(Tag.ErrorUnionType).@"struct".field_names.len +
9665 @typeInfo(Tag.TypeFunction).@"struct".field_names.len +
9666 @intFromBool(arg.noalias_bits != 0) +
9667 params_len);
9668
9669 const func_index = Index.Unwrapped.wrap(.{
9670 .tid = tid,
9671 .index = items.mutate.len + 0,
9672 }, ip);
9673 const error_union_type = Index.Unwrapped.wrap(.{
9674 .tid = tid,
9675 .index = items.mutate.len + 1,
9676 }, ip);
9677 const error_set_type = Index.Unwrapped.wrap(.{
9678 .tid = tid,
9679 .index = items.mutate.len + 2,
9680 }, ip);
9681 const func_ty = Index.Unwrapped.wrap(.{
9682 .tid = tid,
9683 .index = items.mutate.len + 3,
9684 }, ip);
9685
9686 const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
9687 .analysis = .{
9688 .want_runtime_analysis = false,
9689 .branch_hint = .none,
9690 .is_noinline = arg.is_noinline,
9691 .has_error_trace = false,
9692 .inferred_error_set = true,
9693 .disable_instrumentation = false,
9694 .disable_intrinsics = false,
9695 },
9696 // This is populated after we create the Nav below. It is not read
9697 // by equality or hashing functions.
9698 .owner_nav = undefined,
9699 .ty = func_ty,
9700 .branch_quota = 0,
9701 .generic_owner = generic_owner,
9702 });
9703 extra.appendAssumeCapacity(.{@backingInt(Index.none)}); // resolved error set
9704 extra.appendSliceAssumeCapacity(.{@ptrCast(arg.comptime_args)});
9705
9706 const func_type_extra_index = addExtraAssumeCapacity(extra, Tag.TypeFunction{
9707 .params_len = params_len,
9708 .return_type = error_union_type,
9709 .flags = .{
9710 .cc = .pack(generic_owner_ty.cc),
9711 .is_var_args = false,
9712 .has_comptime_bits = false,
9713 .has_noalias_bits = arg.noalias_bits != 0,
9714 .is_noinline = arg.is_noinline,
9715 },
9716 });
9717 // no comptime_bits because has_comptime_bits is false
9718 if (arg.noalias_bits != 0) extra.appendAssumeCapacity(.{arg.noalias_bits});
9719 extra.appendSliceAssumeCapacity(.{@ptrCast(arg.param_types)});
9720
9721 items.appendSliceAssumeCapacity(.{
9722 .tag = &.{
9723 .func_instance,
9724 .type_error_union,
9725 .type_inferred_error_set,
9726 .type_function,
9727 },
9728 .data = &.{
9729 func_extra_index,
9730 addExtraAssumeCapacity(extra, Tag.ErrorUnionType{
9731 .error_set_type = error_set_type,
9732 .payload_type = arg.bare_return_type,
9733 }),
9734 @backingInt(func_index),
9735 func_type_extra_index,
9736 },
9737 });
9738 errdefer {
9739 items.mutate.len -= 4;
9740 extra.mutate.len = prev_extra_len;
9741 }
9742
9743 var func_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
9744 .func = ip.extraFuncInstance(tid, extra.list.*, func_extra_index),
9745 }, 3);
9746 defer func_gop.deinit();
9747 if (func_gop == .existing) {
9748 // Hot path: undo the additions to our two arrays.
9749 items.mutate.len -= 4;
9750 extra.mutate.len = prev_extra_len;
9751 return func_gop.existing;
9752 }
9753 func_gop.putTentative(func_index);
9754 var error_union_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{ .error_union_type = .{
9755 .error_set_type = error_set_type,
9756 .payload_type = arg.bare_return_type,
9757 } }, 2);
9758 defer error_union_type_gop.deinit();
9759 error_union_type_gop.putTentative(error_union_type);
9760 var error_set_type_gop = try ip.getOrPutKeyEnsuringAdditionalCapacity(gpa, io, tid, .{
9761 .inferred_error_set_type = func_index,
9762 }, 1);
9763 defer error_set_type_gop.deinit();
9764 error_set_type_gop.putTentative(error_set_type);
9765 var func_ty_gop = try ip.getOrPutKey(gpa, io, tid, .{
9766 .func_type = extraFuncType(tid, extra.list.*, func_type_extra_index),
9767 });
9768 defer func_ty_gop.deinit();
9769 func_ty_gop.putTentative(func_ty);
9770 try finishFuncInstance(
9771 ip,
9772 gpa,
9773 io,
9774 tid,
9775 extra,
9776 generic_owner,
9777 func_index,
9778 func_extra_index,
9779 arg.anon_name_counter,
9780 );
9781
9782 func_gop.putFinal(func_index);
9783 error_union_type_gop.putFinal(error_union_type);
9784 error_set_type_gop.putFinal(error_set_type);
9785 func_ty_gop.putFinal(func_ty);
9786 return func_index;
9787}
9788
9789fn finishFuncInstance(
9790 ip: *InternPool,
9791 gpa: Allocator,
9792 io: Io,
9793 tid: Zcu.PerThread.Id,
9794 extra: Local.Extra.Mutable,
9795 generic_owner: Index,
9796 func_index: Index,
9797 func_extra_index: u32,
9798 anon_name_counter: *u32,
9799) Allocator.Error!void {
9800 const fn_owner_nav = ip.getNav(ip.funcDeclInfo(generic_owner).owner_nav);
9801 const fn_namespace = fn_owner_nav.analysis.?.namespace;
9802
9803 // TODO: improve this name
9804 const nav_name = try ip.getOrPutStringFmt(gpa, io, tid, "{f}__func_{d}", .{
9805 fn_owner_nav.name.fmt(ip), anon_name_counter.*,
9806 }, .no_embedded_nulls);
9807 anon_name_counter.* += 1;
9808 const nav_fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, io, tid, nav_name);
9809 const nav_index = try ip.createNav(gpa, io, tid, nav_name, nav_fqn, .{
9810 .type = ip.typeOf(func_index),
9811 .@"align" = fn_owner_nav.resolved.?.@"align",
9812 .@"linksection" = fn_owner_nav.resolved.?.@"linksection",
9813 .@"addrspace" = fn_owner_nav.resolved.?.@"addrspace",
9814 .@"const" = true,
9815 .@"threadlocal" = false,
9816 .is_extern_decl = false,
9817 .value = func_index,
9818 });
9819
9820 // Populate the owner_nav field which was left undefined until now.
9821 extra.view().items(.@"0")[
9822 func_extra_index + std.meta.fieldIndex(Tag.FuncInstance, "owner_nav").?
9823 ] = @backingInt(nav_index);
9824}
9825
9826pub fn getIfExists(ip: *const InternPool, key: Key) ?Index {
9827 const full_hash = key.hash64(ip);
9828 const hash: u32 = @truncate(full_hash >> 32);
9829 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
9830 const map = shard.shared.map.acquire();
9831 const map_mask = map.header().mask();
9832 var map_index = hash;
9833 while (true) : (map_index += 1) {
9834 map_index &= map_mask;
9835 const entry = &map.entries[map_index];
9836 const index = entry.acquire();
9837 if (index == .none) return null;
9838 if (entry.hash != hash) continue;
9839 if (ip.indexToKey(index).eql(key, ip)) return index;
9840 }
9841}
9842
9843fn addStringsToMap(
9844 ip: *InternPool,
9845 map_index: MapIndex,
9846 strings: []const NullTerminatedString,
9847) void {
9848 const map = map_index.get(ip);
9849 const adapter: NullTerminatedString.Adapter = .{ .strings = strings };
9850 for (strings) |string| {
9851 const gop = map.getOrPutAssumeCapacityAdapted(string, adapter);
9852 assert(!gop.found_existing);
9853 }
9854}
9855
9856fn addMap(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, cap: usize) Allocator.Error!MapIndex {
9857 const maps = ip.getLocal(tid).getMutableMaps(gpa, io);
9858 const unwrapped: MapIndex.Unwrapped = .{ .tid = tid, .index = maps.mutate.len };
9859 const ptr = try maps.addOne();
9860 errdefer maps.mutate.len = unwrapped.index;
9861 ptr[0].* = .{};
9862 try ptr[0].ensureTotalCapacity(gpa, cap);
9863 return unwrapped.wrap(ip);
9864}
9865
9866/// This operation only happens under compile error conditions.
9867/// Leak the index until the next garbage collection.
9868/// Invalidates all references to this index.
9869pub fn remove(ip: *InternPool, tid: Zcu.PerThread.Id, index: Index) void {
9870 const unwrapped_index = index.unwrap(ip);
9871
9872 if (unwrapped_index.tid == tid) {
9873 const items_len = &ip.getLocal(unwrapped_index.tid).mutate.items.len;
9874 if (unwrapped_index.index == items_len.* - 1) {
9875 // Happy case - we can just drop the item without affecting any other indices.
9876 items_len.* -= 1;
9877 return;
9878 }
9879 }
9880
9881 // We must preserve the item so that indices following it remain valid.
9882 // Thus, we will rewrite the tag to `removed`, leaking the item until
9883 // next GC but causing `KeyAdapter` to ignore it.
9884 const items = ip.getLocalShared(unwrapped_index.tid).items.acquire().view();
9885 @atomicStore(Tag, &items.items(.tag)[unwrapped_index.index], .removed, .unordered);
9886}
9887
9888fn addInt(
9889 ip: *InternPool,
9890 gpa: Allocator,
9891 io: Io,
9892 tid: Zcu.PerThread.Id,
9893 ty: Index,
9894 tag: Tag,
9895 limbs: []const Limb,
9896) !void {
9897 const local = ip.getLocal(tid);
9898 const items_list = local.getMutableItems(gpa, io);
9899 const limbs_list = local.getMutableLimbs(gpa, io);
9900 const limbs_len: u32 = @intCast(limbs.len);
9901 try limbs_list.ensureUnusedCapacity(Int.limbs_items_len + limbs_len);
9902 items_list.appendAssumeCapacity(.{
9903 .tag = tag,
9904 .data = limbs_list.mutate.len,
9905 });
9906 limbs_list.addManyAsArrayAssumeCapacity(Int.limbs_items_len)[0].* = @bitCast(Int{
9907 .ty = ty,
9908 .limbs_len = limbs_len,
9909 });
9910 limbs_list.appendSliceAssumeCapacity(.{limbs});
9911}
9912
9913fn addExtra(extra: Local.Extra.Mutable, item: anytype) Allocator.Error!u32 {
9914 const field_count = @typeInfo(@TypeOf(item)).@"struct".field_names.len;
9915 try extra.ensureUnusedCapacity(field_count);
9916 return addExtraAssumeCapacity(extra, item);
9917}
9918
9919fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
9920 const result: u32 = extra.mutate.len;
9921 const info = @typeInfo(@TypeOf(item)).@"struct";
9922 inline for (info.field_types, info.field_names) |field_type, field_name| {
9923 extra.appendAssumeCapacity(.{switch (field_type) {
9924 Index,
9925 Nav.Index,
9926 Nav.Index.Optional,
9927 NamespaceIndex,
9928 OptionalNamespaceIndex,
9929 MapIndex,
9930 OptionalMapIndex,
9931 String,
9932 NullTerminatedString,
9933 OptionalNullTerminatedString,
9934 Tag.TypePointer.VectorIndex,
9935 TrackedInst.Index,
9936 TrackedInst.Index.Optional,
9937 ComptimeAllocIndex,
9938 => @backingInt(@field(item, field_name)),
9939
9940 u32,
9941 i32,
9942 FuncAnalysis,
9943 Tag.Extern.Flags,
9944 Tag.TypePointer.Flags,
9945 Tag.TypeFunction.Flags,
9946 Tag.TypePointer.PackedOffset,
9947 Tag.TypeUnion.Flags,
9948 Tag.TypeStruct.Flags,
9949 Tag.TypeStructPacked.Bits,
9950 Tag.TypeUnionPacked.Bits,
9951 Tag.TypeEnum.Bits,
9952 Tag.TypeSpirv.Flags,
9953 => @bitCast(@field(item, field_name)),
9954
9955 else => @compileError("bad field type: " ++ @typeName(field_type)),
9956 }});
9957 }
9958 return result;
9959}
9960
9961fn addLimbsExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
9962 switch (@sizeOf(Limb)) {
9963 @sizeOf(u32) => return addExtraAssumeCapacity(ip, extra),
9964 @sizeOf(u64) => {},
9965 else => @compileError("unsupported host"),
9966 }
9967 const result: u32 = @intCast(ip.limbs.items.len);
9968 const info = @typeInfo(@TypeOf(extra)).@"struct";
9969 inline for (info.field_names, info.field_types, 0..) |field_name, field_type, i| {
9970 const new: u32 = switch (field_type) {
9971 u32 => @field(extra, field_name),
9972 Index => @backingInt(@field(extra, field_name)),
9973 else => @compileError("bad field type: " ++ @typeName(field_type)),
9974 };
9975 if (i % 2 == 0) {
9976 ip.limbs.appendAssumeCapacity(new);
9977 } else {
9978 ip.limbs.items[ip.limbs.items.len - 1] |= @as(u64, new) << 32;
9979 }
9980 }
9981 return result;
9982}
9983
9984fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { data: T, end: u32 } {
9985 const extra_items = extra.view().items(.@"0");
9986 var result: T = undefined;
9987 const field_names = @typeInfo(T).@"struct".field_names;
9988 const field_types = @typeInfo(T).@"struct".field_types;
9989 inline for (field_names, field_types, index..) |field_name, field_type, extra_index| {
9990 const extra_item = extra_items[extra_index];
9991 @field(result, field_name) = switch (field_type) {
9992 Index,
9993 Nav.Index,
9994 Nav.Index.Optional,
9995 NamespaceIndex,
9996 OptionalNamespaceIndex,
9997 MapIndex,
9998 OptionalMapIndex,
9999 String,
10000 NullTerminatedString,
10001 OptionalNullTerminatedString,
10002 Tag.TypePointer.VectorIndex,
10003 TrackedInst.Index,
10004 TrackedInst.Index.Optional,
10005 ComptimeAllocIndex,
10006 => @fromBackingInt(@intCast(extra_item)),
10007
10008 u32,
10009 i32,
10010 Tag.Extern.Flags,
10011 Tag.TypePointer.Flags,
10012 Tag.TypeFunction.Flags,
10013 Tag.TypePointer.PackedOffset,
10014 Tag.TypeUnion.Flags,
10015 Tag.TypeStruct.Flags,
10016 FuncAnalysis,
10017 Tag.TypeStructPacked.Bits,
10018 Tag.TypeUnionPacked.Bits,
10019 Tag.TypeEnum.Bits,
10020 Tag.TypeSpirv.Flags,
10021 => @bitCast(extra_item),
10022
10023 else => @compileError("bad field type: " ++ @typeName(field_type)),
10024 };
10025 }
10026 return .{
10027 .data = result,
10028 .end = @intCast(index + field_names.len),
10029 };
10030}
10031
10032fn extraData(extra: Local.Extra, comptime T: type, index: u32) T {
10033 return extraDataTrail(extra, T, index).data;
10034}
10035
10036test "basic usage" {
10037 const gpa = std.testing.allocator;
10038 const io = std.testing.io;
10039
10040 var ip: InternPool = .empty;
10041 try ip.init(gpa, io, 1);
10042 defer ip.deinit(gpa, io);
10043
10044 const i32_type = try ip.get(gpa, io, .main, .{ .int_type = .{
10045 .signedness = .signed,
10046 .bits = 32,
10047 } });
10048 const array_i32 = try ip.get(gpa, io, .main, .{ .array_type = .{
10049 .len = 10,
10050 .child = i32_type,
10051 .sentinel = .none,
10052 } });
10053
10054 const another_i32_type = try ip.get(gpa, io, .main, .{ .int_type = .{
10055 .signedness = .signed,
10056 .bits = 32,
10057 } });
10058 try std.testing.expect(another_i32_type == i32_type);
10059
10060 const another_array_i32 = try ip.get(gpa, io, .main, .{ .array_type = .{
10061 .len = 10,
10062 .child = i32_type,
10063 .sentinel = .none,
10064 } });
10065 try std.testing.expect(another_array_i32 == array_i32);
10066}
10067
10068pub fn childType(ip: *const InternPool, i: Index) Index {
10069 return switch (ip.indexToKey(i)) {
10070 .ptr_type => |ptr_type| ptr_type.child,
10071 .vector_type => |vector_type| vector_type.child,
10072 .array_type => |array_type| array_type.child,
10073 .opt_type, .anyframe_type => |child| child,
10074 .spirv_type => blk: {
10075 const info = ip.loadSpirvType(i);
10076 assert(info.flags.tag == .runtime_array);
10077 break :blk info.ty;
10078 },
10079 else => unreachable,
10080 };
10081}
10082
10083/// Given a slice type, returns the type of the ptr field.
10084pub fn slicePtrType(ip: *const InternPool, index: Index) Index {
10085 switch (index) {
10086 .slice_const_u8_type => return .manyptr_const_u8_type,
10087 .slice_const_u8_sentinel_0_type => return .manyptr_const_u8_sentinel_0_type,
10088 .slice_const_slice_const_u8_type => return .manyptr_const_slice_const_u8_type,
10089 .slice_const_type_type => return .manyptr_const_type_type,
10090 else => {},
10091 }
10092 const item = index.unwrap(ip).getItem(ip);
10093 switch (item.tag) {
10094 .type_slice => return @fromBackingInt(@intCast(item.data)),
10095 else => unreachable, // not a slice type
10096 }
10097}
10098
10099/// Given a slice value, returns the value of the ptr field.
10100pub fn slicePtr(ip: *const InternPool, index: Index) Index {
10101 const unwrapped_index = index.unwrap(ip);
10102 const item = unwrapped_index.getItem(ip);
10103 switch (item.tag) {
10104 .ptr_slice => return extraData(unwrapped_index.getExtra(ip), PtrSlice, item.data).ptr,
10105 else => unreachable, // not a slice value
10106 }
10107}
10108
10109/// Given a slice value, returns the value of the len field.
10110pub fn sliceLen(ip: *const InternPool, index: Index) Index {
10111 const unwrapped_index = index.unwrap(ip);
10112 const item = unwrapped_index.getItem(ip);
10113 switch (item.tag) {
10114 .ptr_slice => return extraData(unwrapped_index.getExtra(ip), PtrSlice, item.data).len,
10115 else => unreachable, // not a slice value
10116 }
10117}
10118
10119/// Given an existing value, returns the same value but with the supplied type.
10120/// Only some combinations are allowed:
10121/// * identity coercion
10122/// * undef => any
10123/// * int <=> int
10124/// * int <=> enum
10125/// * enum_literal => enum
10126/// * float <=> float
10127/// * ptr <=> ptr
10128/// * opt ptr <=> ptr
10129/// * opt ptr <=> opt ptr
10130/// * int <=> ptr
10131/// * null_value => opt
10132/// * payload => opt
10133/// * error set <=> error set
10134/// * error union <=> error union
10135/// * error set => error union
10136/// * payload => error union
10137/// * fn <=> fn
10138/// * aggregate <=> aggregate (where children can also be coerced)
10139pub fn getCoerced(
10140 ip: *InternPool,
10141 gpa: Allocator,
10142 io: Io,
10143 tid: Zcu.PerThread.Id,
10144 val: Index,
10145 new_ty: Index,
10146) Allocator.Error!Index {
10147 const old_ty = ip.typeOf(val);
10148 if (old_ty == new_ty) return val;
10149
10150 switch (val) {
10151 .undef => return ip.get(gpa, io, tid, .{ .undef = new_ty }),
10152 .null_value => {
10153 if (ip.isOptionalType(new_ty)) return ip.get(gpa, io, tid, .{ .opt = .{
10154 .ty = new_ty,
10155 .val = .none,
10156 } });
10157
10158 if (ip.isPointerType(new_ty)) switch (ip.indexToKey(new_ty).ptr_type.flags.size) {
10159 .one, .many, .c => return ip.get(gpa, io, tid, .{ .ptr = .{
10160 .ty = new_ty,
10161 .base_addr = .int,
10162 .byte_offset = 0,
10163 } }),
10164 .slice => return ip.get(gpa, io, tid, .{ .slice = .{
10165 .ty = new_ty,
10166 .ptr = try ip.get(gpa, io, tid, .{ .ptr = .{
10167 .ty = ip.slicePtrType(new_ty),
10168 .base_addr = .int,
10169 .byte_offset = 0,
10170 } }),
10171 .len = .undef_usize,
10172 } }),
10173 };
10174 },
10175 else => {
10176 const unwrapped_val = val.unwrap(ip);
10177 const val_item = unwrapped_val.getItem(ip);
10178 switch (val_item.tag) {
10179 .func_decl => return getCoercedFuncDecl(ip, gpa, io, tid, val, new_ty),
10180 .func_instance => return getCoercedFuncInstance(ip, gpa, io, tid, val, new_ty),
10181 .func_coerced => {
10182 const func: Index = @fromBackingInt(@intCast(unwrapped_val.getExtra(ip).view().items(.@"0")[
10183 val_item.data + std.meta.fieldIndex(Tag.FuncCoerced, "func").?
10184 ]));
10185 switch (func.unwrap(ip).getTag(ip)) {
10186 .func_decl => return getCoercedFuncDecl(ip, gpa, io, tid, func, new_ty),
10187 .func_instance => return getCoercedFuncInstance(ip, gpa, io, tid, func, new_ty),
10188 else => unreachable,
10189 }
10190 },
10191 else => {},
10192 }
10193 },
10194 }
10195
10196 switch (ip.indexToKey(val)) {
10197 .undef => return ip.get(gpa, io, tid, .{ .undef = new_ty }),
10198 .func => unreachable,
10199
10200 .int => |int| switch (ip.indexToKey(new_ty)) {
10201 .enum_type => return ip.get(gpa, io, tid, .{ .enum_tag = .{
10202 .ty = new_ty,
10203 .int = try ip.getCoerced(gpa, io, tid, val, ip.loadEnumType(new_ty).int_tag_type),
10204 } }),
10205 .ptr_type => switch (int.storage) {
10206 inline .u64, .i64 => |int_val| return ip.get(gpa, io, tid, .{ .ptr = .{
10207 .ty = new_ty,
10208 .base_addr = .int,
10209 .byte_offset = @intCast(int_val),
10210 } }),
10211 .big_int => unreachable, // must be a usize
10212 },
10213 else => if (ip.isIntegerType(new_ty))
10214 return ip.getCoercedInts(gpa, io, tid, int, new_ty),
10215 },
10216 .float => |float| switch (ip.indexToKey(new_ty)) {
10217 .simple_type => |simple| switch (simple) {
10218 .f16,
10219 .f32,
10220 .f64,
10221 .f80,
10222 .f128,
10223 .c_longdouble,
10224 .comptime_float,
10225 => return ip.get(gpa, io, tid, .{ .float = .{
10226 .ty = new_ty,
10227 .storage = float.storage,
10228 } }),
10229 else => {},
10230 },
10231 else => {},
10232 },
10233 .enum_tag => |enum_tag| if (ip.isIntegerType(new_ty))
10234 return ip.getCoercedInts(gpa, io, tid, ip.indexToKey(enum_tag.int).int, new_ty),
10235 .enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) {
10236 .enum_type => {
10237 const enum_type = ip.loadEnumType(new_ty);
10238 const index = enum_type.nameIndex(ip, enum_literal).?;
10239 assert(enum_type.int_tag_type != .noreturn_type);
10240 return ip.get(gpa, io, tid, .{ .enum_tag = .{
10241 .ty = new_ty,
10242 .int = if (enum_type.field_values.len != 0)
10243 enum_type.field_values.get(ip)[index]
10244 else
10245 try ip.get(gpa, io, tid, .{ .int = .{
10246 .ty = enum_type.int_tag_type,
10247 .storage = .{ .u64 = index },
10248 } }),
10249 } });
10250 },
10251 else => {},
10252 },
10253 .slice => |slice| if (ip.isPointerType(new_ty) and ip.indexToKey(new_ty).ptr_type.flags.size == .slice)
10254 return ip.get(gpa, io, tid, .{ .slice = .{
10255 .ty = new_ty,
10256 .ptr = try ip.getCoerced(gpa, io, tid, slice.ptr, ip.slicePtrType(new_ty)),
10257 .len = slice.len,
10258 } })
10259 else if (ip.isIntegerType(new_ty))
10260 return ip.getCoerced(gpa, io, tid, slice.ptr, new_ty),
10261 .ptr => |ptr| if (ip.isPointerType(new_ty) and ip.indexToKey(new_ty).ptr_type.flags.size != .slice)
10262 return ip.get(gpa, io, tid, .{ .ptr = .{
10263 .ty = new_ty,
10264 .base_addr = ptr.base_addr,
10265 .byte_offset = ptr.byte_offset,
10266 } })
10267 else if (ip.isIntegerType(new_ty))
10268 switch (ptr.base_addr) {
10269 .int => return ip.get(gpa, io, tid, .{ .int = .{
10270 .ty = .usize_type,
10271 .storage = .{ .u64 = @intCast(ptr.byte_offset) },
10272 } }),
10273 else => {},
10274 },
10275 .opt => |opt| switch (ip.indexToKey(new_ty)) {
10276 .ptr_type => |ptr_type| return switch (opt.val) {
10277 .none => switch (ptr_type.flags.size) {
10278 .one, .many, .c => try ip.get(gpa, io, tid, .{ .ptr = .{
10279 .ty = new_ty,
10280 .base_addr = .int,
10281 .byte_offset = 0,
10282 } }),
10283 .slice => try ip.get(gpa, io, tid, .{ .slice = .{
10284 .ty = new_ty,
10285 .ptr = try ip.get(gpa, io, tid, .{ .ptr = .{
10286 .ty = ip.slicePtrType(new_ty),
10287 .base_addr = .int,
10288 .byte_offset = 0,
10289 } }),
10290 .len = .undef_usize,
10291 } }),
10292 },
10293 else => |payload| try ip.getCoerced(gpa, io, tid, payload, new_ty),
10294 },
10295 .opt_type => |child_type| return try ip.get(gpa, io, tid, .{ .opt = .{
10296 .ty = new_ty,
10297 .val = switch (opt.val) {
10298 .none => .none,
10299 else => try ip.getCoerced(gpa, io, tid, opt.val, child_type),
10300 },
10301 } }),
10302 else => {},
10303 },
10304 .err => |err| if (ip.isErrorSetType(new_ty))
10305 return ip.get(gpa, io, tid, .{ .err = .{
10306 .ty = new_ty,
10307 .name = err.name,
10308 } })
10309 else if (ip.isErrorUnionType(new_ty))
10310 return ip.get(gpa, io, tid, .{ .error_union = .{
10311 .ty = new_ty,
10312 .val = .{ .err_name = err.name },
10313 } }),
10314 .error_union => |error_union| if (ip.isErrorUnionType(new_ty))
10315 return ip.get(gpa, io, tid, .{ .error_union = .{
10316 .ty = new_ty,
10317 .val = error_union.val,
10318 } }),
10319 .aggregate => |aggregate| {
10320 const new_len: usize = @intCast(ip.aggregateTypeLen(new_ty));
10321 direct: {
10322 const old_ty_child = switch (ip.indexToKey(old_ty)) {
10323 inline .array_type, .vector_type => |seq_type| seq_type.child,
10324 .tuple_type, .struct_type => break :direct,
10325 else => unreachable,
10326 };
10327 const new_ty_child = switch (ip.indexToKey(new_ty)) {
10328 inline .array_type, .vector_type => |seq_type| seq_type.child,
10329 .tuple_type, .struct_type => break :direct,
10330 else => unreachable,
10331 };
10332 if (old_ty_child != new_ty_child) break :direct;
10333 switch (aggregate.storage) {
10334 .bytes => |bytes| return ip.get(gpa, io, tid, .{ .aggregate = .{
10335 .ty = new_ty,
10336 .storage = .{ .bytes = bytes },
10337 } }),
10338 .elems => |elems| {
10339 const elems_copy = try gpa.dupe(Index, elems[0..new_len]);
10340 defer gpa.free(elems_copy);
10341 return ip.get(gpa, io, tid, .{ .aggregate = .{
10342 .ty = new_ty,
10343 .storage = .{ .elems = elems_copy },
10344 } });
10345 },
10346 .repeated_elem => |elem| {
10347 return ip.get(gpa, io, tid, .{ .aggregate = .{
10348 .ty = new_ty,
10349 .storage = .{ .repeated_elem = elem },
10350 } });
10351 },
10352 }
10353 }
10354 // Direct approach failed - we must recursively coerce elems
10355 const agg_elems = try gpa.alloc(Index, new_len);
10356 defer gpa.free(agg_elems);
10357 // First, fill the vector with the uncoerced elements. We do this to avoid key
10358 // lifetime issues, since it'll allow us to avoid referencing `aggregate` after we
10359 // begin interning elems.
10360 switch (aggregate.storage) {
10361 .bytes => |bytes| {
10362 // We have to intern each value here, so unfortunately we can't easily avoid
10363 // the repeated indexToKey calls.
10364 for (agg_elems, 0..) |*elem, index| {
10365 elem.* = try ip.get(gpa, io, tid, .{ .int = .{
10366 .ty = .u8_type,
10367 .storage = .{ .u64 = bytes.at(index, ip) },
10368 } });
10369 }
10370 },
10371 .elems => |elems| @memcpy(agg_elems, elems[0..new_len]),
10372 .repeated_elem => |elem| @memset(agg_elems, elem),
10373 }
10374 // Now, coerce each element to its new type.
10375 for (agg_elems, 0..) |*elem, i| {
10376 const new_elem_ty = switch (ip.indexToKey(new_ty)) {
10377 inline .array_type, .vector_type => |seq_type| seq_type.child,
10378 .tuple_type => |tuple_type| tuple_type.types.get(ip)[i],
10379 .struct_type => ip.loadStructType(new_ty).field_types.get(ip)[i],
10380 else => unreachable,
10381 };
10382 elem.* = try ip.getCoerced(gpa, io, tid, elem.*, new_elem_ty);
10383 }
10384 return ip.get(gpa, io, tid, .{ .aggregate = .{ .ty = new_ty, .storage = .{ .elems = agg_elems } } });
10385 },
10386 else => {},
10387 }
10388
10389 switch (ip.indexToKey(new_ty)) {
10390 .opt_type => |child_type| switch (val) {
10391 .null_value => return ip.get(gpa, io, tid, .{ .opt = .{
10392 .ty = new_ty,
10393 .val = .none,
10394 } }),
10395 else => return ip.get(gpa, io, tid, .{ .opt = .{
10396 .ty = new_ty,
10397 .val = try ip.getCoerced(gpa, io, tid, val, child_type),
10398 } }),
10399 },
10400 .error_union_type => |error_union_type| return ip.get(gpa, io, tid, .{ .error_union = .{
10401 .ty = new_ty,
10402 .val = .{ .payload = try ip.getCoerced(gpa, io, tid, val, error_union_type.payload_type) },
10403 } }),
10404 else => {},
10405 }
10406 if (std.debug.runtime_safety) {
10407 std.debug.panic("InternPool.getCoerced of {s} not implemented from {s} to {s}", .{
10408 @tagName(ip.indexToKey(val)),
10409 @tagName(ip.indexToKey(old_ty)),
10410 @tagName(ip.indexToKey(new_ty)),
10411 });
10412 }
10413 unreachable;
10414}
10415
10416fn getCoercedFuncDecl(
10417 ip: *InternPool,
10418 gpa: Allocator,
10419 io: Io,
10420 tid: Zcu.PerThread.Id,
10421 val: Index,
10422 new_ty: Index,
10423) Allocator.Error!Index {
10424 const unwrapped_val = val.unwrap(ip);
10425 const prev_ty: Index = @fromBackingInt(@intCast(unwrapped_val.getExtra(ip).view().items(.@"0")[
10426 unwrapped_val.getData(ip) + std.meta.fieldIndex(Tag.FuncDecl, "ty").?
10427 ]));
10428 if (new_ty == prev_ty) return val;
10429 return getCoercedFunc(ip, gpa, io, tid, val, new_ty);
10430}
10431
10432fn getCoercedFuncInstance(
10433 ip: *InternPool,
10434 gpa: Allocator,
10435 io: Io,
10436 tid: Zcu.PerThread.Id,
10437 val: Index,
10438 new_ty: Index,
10439) Allocator.Error!Index {
10440 const unwrapped_val = val.unwrap(ip);
10441 const prev_ty: Index = @fromBackingInt(@intCast(unwrapped_val.getExtra(ip).view().items(.@"0")[
10442 unwrapped_val.getData(ip) + std.meta.fieldIndex(Tag.FuncInstance, "ty").?
10443 ]));
10444 if (new_ty == prev_ty) return val;
10445 return getCoercedFunc(ip, gpa, io, tid, val, new_ty);
10446}
10447
10448fn getCoercedFunc(
10449 ip: *InternPool,
10450 gpa: Allocator,
10451 io: Io,
10452 tid: Zcu.PerThread.Id,
10453 func: Index,
10454 ty: Index,
10455) Allocator.Error!Index {
10456 const local = ip.getLocal(tid);
10457 const items = local.getMutableItems(gpa, io);
10458 try items.ensureUnusedCapacity(1);
10459 const extra = local.getMutableExtra(gpa, io);
10460
10461 const prev_extra_len = extra.mutate.len;
10462 try extra.ensureUnusedCapacity(@typeInfo(Tag.FuncCoerced).@"struct".field_names.len);
10463
10464 const extra_index = addExtraAssumeCapacity(extra, Tag.FuncCoerced{
10465 .ty = ty,
10466 .func = func,
10467 });
10468 errdefer extra.mutate.len = prev_extra_len;
10469
10470 var gop = try ip.getOrPutKey(gpa, io, tid, .{
10471 .func = ip.extraFuncCoerced(extra.list.*, extra_index),
10472 });
10473 defer gop.deinit();
10474 if (gop == .existing) {
10475 extra.mutate.len = prev_extra_len;
10476 return gop.existing;
10477 }
10478
10479 items.appendAssumeCapacity(.{
10480 .tag = .func_coerced,
10481 .data = extra_index,
10482 });
10483 return gop.put();
10484}
10485
10486/// Asserts `val` has an integer type.
10487/// Assumes `new_ty` is an integer type.
10488pub fn getCoercedInts(
10489 ip: *InternPool,
10490 gpa: Allocator,
10491 io: Io,
10492 tid: Zcu.PerThread.Id,
10493 int: Key.Int,
10494 new_ty: Index,
10495) Allocator.Error!Index {
10496 return ip.get(gpa, io, tid, .{ .int = .{
10497 .ty = new_ty,
10498 .storage = int.storage,
10499 } });
10500}
10501
10502pub fn indexToFuncType(ip: *const InternPool, val: Index) ?Key.FuncType {
10503 const unwrapped_val = val.unwrap(ip);
10504 const item = unwrapped_val.getItem(ip);
10505 switch (item.tag) {
10506 .type_function => return extraFuncType(unwrapped_val.tid, unwrapped_val.getExtra(ip), item.data),
10507 else => return null,
10508 }
10509}
10510
10511/// includes .comptime_int_type
10512pub fn isIntegerType(ip: *const InternPool, ty: Index) bool {
10513 return switch (ty) {
10514 .usize_type,
10515 .isize_type,
10516 .c_char_type,
10517 .c_short_type,
10518 .c_ushort_type,
10519 .c_int_type,
10520 .c_uint_type,
10521 .c_long_type,
10522 .c_ulong_type,
10523 .c_longlong_type,
10524 .c_ulonglong_type,
10525 .comptime_int_type,
10526 => true,
10527 else => switch (ty.unwrap(ip).getTag(ip)) {
10528 .type_int_signed,
10529 .type_int_unsigned,
10530 => true,
10531 else => false,
10532 },
10533 };
10534}
10535
10536/// does not include .enum_literal_type
10537pub fn isEnumType(ip: *const InternPool, ty: Index) bool {
10538 return ip.indexToKey(ty) == .enum_type;
10539}
10540
10541pub fn isUnion(ip: *const InternPool, ty: Index) bool {
10542 return ip.indexToKey(ty) == .union_type;
10543}
10544
10545pub fn isFunctionType(ip: *const InternPool, ty: Index) bool {
10546 return ip.indexToKey(ty) == .func_type;
10547}
10548
10549pub fn isPointerType(ip: *const InternPool, ty: Index) bool {
10550 return ip.indexToKey(ty) == .ptr_type;
10551}
10552
10553pub fn isOptionalType(ip: *const InternPool, ty: Index) bool {
10554 return ip.indexToKey(ty) == .opt_type;
10555}
10556
10557/// includes .inferred_error_set_type
10558pub fn isErrorSetType(ip: *const InternPool, ty: Index) bool {
10559 return switch (ty) {
10560 .anyerror_type, .adhoc_inferred_error_set_type => true,
10561 else => switch (ip.indexToKey(ty)) {
10562 .error_set_type, .inferred_error_set_type => true,
10563 else => false,
10564 },
10565 };
10566}
10567
10568pub fn isInferredErrorSetType(ip: *const InternPool, ty: Index) bool {
10569 return ty == .adhoc_inferred_error_set_type or ip.indexToKey(ty) == .inferred_error_set_type;
10570}
10571
10572pub fn isErrorUnionType(ip: *const InternPool, ty: Index) bool {
10573 return ip.indexToKey(ty) == .error_union_type;
10574}
10575
10576pub fn isAggregateType(ip: *const InternPool, ty: Index) bool {
10577 return switch (ip.indexToKey(ty)) {
10578 .array_type, .vector_type, .tuple_type, .struct_type => true,
10579 else => false,
10580 };
10581}
10582
10583pub fn errorUnionSet(ip: *const InternPool, ty: Index) Index {
10584 return ip.indexToKey(ty).error_union_type.error_set_type;
10585}
10586
10587pub fn errorUnionPayload(ip: *const InternPool, ty: Index) Index {
10588 return ip.indexToKey(ty).error_union_type.payload_type;
10589}
10590
10591pub fn dump(ip: *const InternPool) void {
10592 var buffer: [4096]u8 = undefined;
10593 const stderr = std.debug.lockStderr(&buffer);
10594 defer std.debug.unlockStderr();
10595 const w = &stderr.file_writer.interface;
10596 dumpDependencyStatsFallible(ip, w) catch return;
10597 dumpStatsFallible(ip, w, std.heap.page_allocator) catch return;
10598 dumpAllFallible(ip, w) catch return;
10599}
10600
10601fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
10602 const dep_entries_len = ip.dep_entries.items.len - ip.free_dep_entries.items.len;
10603 const src_hash_deps_len = ip.src_hash_deps.count();
10604 const nav_val_deps_len = ip.nav_val_deps.count();
10605 const nav_ty_deps_len = ip.nav_ty_deps.count();
10606 const func_ies_deps_len = ip.func_ies_deps.count();
10607 const type_layout_deps_len = ip.type_layout_deps.count();
10608 const struct_defaults_deps_len = ip.struct_defaults_deps.count();
10609 const source_file_deps_len = ip.source_file_deps.count();
10610 const embed_file_deps_len = ip.embed_file_deps.count();
10611 const namespace_deps_len = ip.namespace_deps.count();
10612 const namespace_name_deps_len = ip.namespace_name_deps.count();
10613 const dep_entries_size = dep_entries_len * @sizeOf(DepEntry);
10614 const src_hash_deps_size = src_hash_deps_len * 8;
10615 const nav_val_deps_size = nav_val_deps_len * 8;
10616 const nav_ty_deps_size = nav_ty_deps_len * 8;
10617 const func_ies_deps_size = func_ies_deps_len * 8;
10618 const type_layout_deps_size = type_layout_deps_len * 8;
10619 const struct_defaults_deps_size = struct_defaults_deps_len * 8;
10620 const source_file_deps_size = source_file_deps_len * 8;
10621 const embed_file_deps_size = embed_file_deps_len * 8;
10622 const namespace_deps_size = namespace_deps_len * 8;
10623 const namespace_name_deps_size = namespace_name_deps_len * (@sizeOf(NamespaceNameKey) + 4);
10624
10625 try w.print(
10626 \\InternPool dependencies: {d} bytes
10627 \\ {d} entries: {d} bytes
10628 \\ {d} src_hash: {d} bytes
10629 \\ {d} nav_val: {d} bytes
10630 \\ {d} nav_ty: {d} bytes
10631 \\ {d} func_ies: {d} bytes
10632 \\ {d} type_layout: {d} bytes
10633 \\ {d} struct_defaults: {d} bytes
10634 \\ {d} source_file: {d} bytes
10635 \\ {d} embed_file: {d} bytes
10636 \\ {d} namespace: {d} bytes
10637 \\ {d} namespace_name: {d} bytes
10638 \\
10639 , .{
10640 dep_entries_size + src_hash_deps_size + nav_val_deps_size + nav_ty_deps_size +
10641 func_ies_deps_size + type_layout_deps_size + struct_defaults_deps_size + source_file_deps_size +
10642 embed_file_deps_size + namespace_deps_size + namespace_name_deps_size,
10643 dep_entries_len,
10644 dep_entries_size,
10645 src_hash_deps_len,
10646 src_hash_deps_size,
10647 nav_val_deps_len,
10648 nav_val_deps_size,
10649 nav_ty_deps_len,
10650 nav_ty_deps_size,
10651 func_ies_deps_len,
10652 func_ies_deps_size,
10653 type_layout_deps_len,
10654 type_layout_deps_size,
10655 struct_defaults_deps_len,
10656 struct_defaults_deps_size,
10657 source_file_deps_len,
10658 source_file_deps_size,
10659 embed_file_deps_len,
10660 embed_file_deps_size,
10661 namespace_deps_len,
10662 namespace_deps_size,
10663 namespace_name_deps_len,
10664 namespace_name_deps_size,
10665 });
10666}
10667
10668fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !void {
10669 var items_len: usize = 0;
10670 var extra_len: usize = 0;
10671 var limbs_len: usize = 0;
10672 for (ip.locals) |*local| {
10673 items_len += local.mutate.items.len;
10674 extra_len += local.mutate.extra.len;
10675 limbs_len += local.mutate.limbs.len;
10676 }
10677 const items_size = (1 + 4) * items_len;
10678 const extra_size = 4 * extra_len;
10679 const limbs_size = 8 * limbs_len;
10680
10681 // TODO: map overhead size is not taken into account
10682 const total_size = items_size + extra_size + limbs_size;
10683
10684 try w.print(
10685 \\InternPool values: {d} bytes
10686 \\ {d} items: {d} bytes
10687 \\ {d} extra: {d} bytes
10688 \\ {d} limbs: {d} bytes
10689 \\
10690 , .{
10691 total_size,
10692 items_len,
10693 items_size,
10694 extra_len,
10695 extra_size,
10696 limbs_len,
10697 limbs_size,
10698 });
10699
10700 const TagStats = struct {
10701 count: usize = 0,
10702 bytes: usize = 0,
10703 };
10704 var counts: std.array_hash_map.Auto(Tag, TagStats) = .empty;
10705 for (ip.locals) |*local| {
10706 // Early check for length 0, because `view()` is invalid if capacity is 0
10707 if (local.mutate.items.len == 0) continue;
10708 const items = local.shared.items.view().slice();
10709 const extra_list = local.shared.extra;
10710 const extra_items = extra_list.view().items(.@"0");
10711 for (
10712 items.items(.tag)[0..local.mutate.items.len],
10713 items.items(.data)[0..local.mutate.items.len],
10714 ) |tag, data| {
10715 const gop = try counts.getOrPut(arena, tag);
10716 if (!gop.found_existing) gop.value_ptr.* = .{};
10717 gop.value_ptr.count += 1;
10718 gop.value_ptr.bytes += 1 + 4 + @as(usize, switch (tag) {
10719 // Note that in this case, we have technically leaked some extra data
10720 // bytes which we do not account for here.
10721 .removed => 0,
10722
10723 .type_int_signed => 0,
10724 .type_int_unsigned => 0,
10725 .type_array_small => @sizeOf(Vector),
10726 .type_array_big => @sizeOf(Array),
10727 .type_vector => @sizeOf(Vector),
10728 .type_pointer => @sizeOf(Tag.TypePointer),
10729 .type_slice => 0,
10730 .type_optional => 0,
10731 .type_anyframe => 0,
10732 .type_error_union => @sizeOf(Key.ErrorUnionType),
10733 .type_spirv => @sizeOf(Tag.TypeSpirv),
10734 .type_anyerror_union => 0,
10735 .type_error_set => b: {
10736 const info = extraData(extra_list, Tag.ErrorSet, data);
10737 break :b @sizeOf(Tag.ErrorSet) + (@sizeOf(u32) * info.names_len);
10738 },
10739 .type_inferred_error_set => 0,
10740 .type_tuple => b: {
10741 const info = extraData(extra_list, TypeTuple, data);
10742 break :b @sizeOf(TypeTuple) + (@sizeOf(u32) * 2 * info.fields_len);
10743 },
10744 .type_function => b: {
10745 const info = extraData(extra_list, Tag.TypeFunction, data);
10746 break :b @sizeOf(Tag.TypeFunction) +
10747 (@sizeOf(Index) * info.params_len) +
10748 (@as(u32, 4) * info.flags.cc.extraLen()) +
10749 (@as(u32, 4) * @intFromBool(info.flags.has_comptime_bits)) +
10750 (@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits));
10751 },
10752
10753 .type_struct => b: {
10754 var n: usize = @typeInfo(Tag.TypeStruct).@"struct".field_names.len;
10755 const extra = extraDataTrail(extra_list, Tag.TypeStruct, data);
10756 switch (extra.data.flags.any_captures) {
10757 .reified => n += 2, // type_hash: PackedU64
10758 .true => {
10759 n += 1; // captures_len: u32
10760 n += extra_items[extra.end]; // capture: CaptureValue
10761 },
10762 .false => {},
10763 }
10764 n += extra.data.fields_len; // field_name: NullTerminatedString
10765 n += extra.data.fields_len; // field_type: Index
10766 if (extra.data.flags.any_field_defaults) {
10767 n += extra.data.fields_len; // field_default: Index
10768 }
10769 if (extra.data.flags.any_field_aligns) {
10770 n += (extra.data.fields_len + 3) / 4; // field_align: Alignment
10771 }
10772 if (extra.data.flags.any_comptime_fields) {
10773 n += (extra.data.fields_len + 31) / 32; // field_is_comptime_bits: u32
10774 }
10775 if (extra.data.flags.layout == .auto) {
10776 n += extra.data.fields_len; // field_runtime_order: RuntimeOrder
10777 }
10778 n += extra.data.fields_len; // field_offset: u32
10779 break :b n * @sizeOf(u32);
10780 },
10781 .type_struct_packed_auto, .type_struct_packed_explicit => b: {
10782 var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".field_names.len;
10783 const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
10784 switch (extra.data.bits.captures_len) {
10785 .reified => n += 2, // type_hash: PackedU64
10786 _ => |len| n += @backingInt(len), // capture: CaptureValue
10787 }
10788 n += extra.data.fields_len; // field_name: NullTerminatedString
10789 n += extra.data.fields_len; // field_type: Index
10790 break :b n * @sizeOf(u32);
10791 },
10792 .type_struct_packed_auto_defaults, .type_struct_packed_explicit_defaults => b: {
10793 var n: usize = @typeInfo(Tag.TypeStructPacked).@"struct".field_names.len;
10794 const extra = extraDataTrail(extra_list, Tag.TypeStructPacked, data);
10795 switch (extra.data.bits.captures_len) {
10796 .reified => n += 2, // type_hash: PackedU64
10797 _ => |len| n += @backingInt(len), // capture: CaptureValue
10798 }
10799 n += extra.data.fields_len; // field_name: NullTerminatedString
10800 n += extra.data.fields_len; // field_type: Index
10801 n += extra.data.fields_len; // field_default: Index
10802 break :b n * @sizeOf(u32);
10803 },
10804 .type_union => b: {
10805 var n: usize = @typeInfo(Tag.TypeUnion).@"struct".field_names.len;
10806 const extra = extraDataTrail(extra_list, Tag.TypeUnion, data);
10807 switch (extra.data.flags.any_captures) {
10808 .reified => n += 2, // type_hash: PackedU64
10809 .true => {
10810 n += 1; // captures_len: u32
10811 n += extra_items[extra.end]; // capture: CaptureValue
10812 },
10813 .false => {},
10814 }
10815 n += extra.data.fields_len; // field_type: Index
10816 if (extra.data.flags.any_field_aligns) {
10817 n += (extra.data.fields_len + 3) / 4; // field_align: Alignment
10818 }
10819 break :b n * @sizeOf(u32);
10820 },
10821 .type_union_packed_auto, .type_union_packed_explicit => b: {
10822 var n: usize = @typeInfo(Tag.TypeUnionPacked).@"struct".field_names.len;
10823 const extra = extraDataTrail(extra_list, Tag.TypeUnionPacked, data);
10824 switch (extra.data.bits.captures_len) {
10825 .reified => n += 2, // type_hash: PackedU64
10826 _ => |len| n += @backingInt(len), // capture: CaptureValue
10827 }
10828 n += extra.data.fields_len; // field_type: Index
10829 break :b n * @sizeOf(u32);
10830 },
10831 .type_enum_auto => b: {
10832 var n: usize = @typeInfo(Tag.TypeEnum).@"struct".field_names.len;
10833 const extra = extraData(extra_list, Tag.TypeEnum, data);
10834 switch (extra.bits.captures_len) {
10835 .generated_union_tag => n += 1, // owner_union: Index
10836 .reified => {
10837 n += 1; // zir_index: TrackedInst.Index,
10838 n += 2; // type_hash: PackedU64
10839 },
10840 _ => |len| {
10841 n += 1; // zir_index: TrackedInst.Index,
10842 n += @backingInt(len); // capture: CaptureValue
10843 },
10844 }
10845 n += extra.fields_len; // field_name: NullTerminatedString
10846 break :b n * @sizeOf(u32);
10847 },
10848 .type_enum_explicit, .type_enum_nonexhaustive => b: {
10849 var n: usize = @typeInfo(Tag.TypeEnum).@"struct".field_names.len;
10850 const extra = extraData(extra_list, Tag.TypeEnum, data);
10851 switch (extra.bits.captures_len) {
10852 .generated_union_tag => n += 1, // owner_union: Index
10853 .reified => {
10854 n += 1; // zir_index: TrackedInst.Index,
10855 n += 2; // type_hash: PackedU64
10856 },
10857 _ => |len| {
10858 n += 1; // zir_index: TrackedInst.Index,
10859 n += @backingInt(len); // capture: CaptureValue
10860 },
10861 }
10862 n += 1; // field_value_map: MapIndex
10863 n += extra.fields_len; // field_name: NullTerminatedString
10864 n += extra.fields_len; // field_value: Index
10865 break :b n * @sizeOf(u32);
10866 },
10867 .type_opaque => b: {
10868 var n: usize = @typeInfo(Tag.TypeEnum).@"struct".field_names.len;
10869 const extra = extraData(extra_list, Tag.TypeOpaque, data);
10870 n += extra.captures_len; // capture: CaptureValue
10871 break :b n * @sizeOf(u32);
10872 },
10873
10874 .undef => 0,
10875 .simple_type => 0,
10876 .simple_value => 0,
10877 .ptr_nav => @sizeOf(PtrNav),
10878 .ptr_comptime_alloc => @sizeOf(PtrComptimeAlloc),
10879 .ptr_uav => @sizeOf(PtrUav),
10880 .ptr_uav_aligned => @sizeOf(PtrUavAligned),
10881 .ptr_comptime_field => @sizeOf(PtrComptimeField),
10882 .ptr_int => @sizeOf(PtrInt),
10883 .ptr_eu_payload => @sizeOf(PtrBase),
10884 .ptr_opt_payload => @sizeOf(PtrBase),
10885 .ptr_elem => @sizeOf(PtrBaseIndex),
10886 .ptr_field => @sizeOf(PtrBaseIndex),
10887 .ptr_slice => @sizeOf(PtrSlice),
10888 .opt_null => 0,
10889 .opt_payload => @sizeOf(Tag.TypeValue),
10890 .int_u8 => 0,
10891 .int_u16 => 0,
10892 .int_u32 => 0,
10893 .int_i32 => 0,
10894 .int_usize => 0,
10895 .int_comptime_int_u32 => 0,
10896 .int_comptime_int_i32 => 0,
10897 .int_small => @sizeOf(IntSmall),
10898
10899 .int_positive,
10900 .int_negative,
10901 => b: {
10902 const limbs_list = local.shared.getLimbs();
10903 const int: Int = @bitCast(limbs_list.view().items(.@"0")[data..][0..Int.limbs_items_len].*);
10904 break :b @sizeOf(Int) + int.limbs_len * @sizeOf(Limb);
10905 },
10906
10907 .error_set_error, .error_union_error => @sizeOf(Key.Error),
10908 .error_union_payload => @sizeOf(Tag.TypeValue),
10909 .enum_literal => 0,
10910 .enum_tag => @sizeOf(Tag.EnumTag),
10911
10912 .bytes => b: {
10913 const info = extraData(extra_list, Bytes, data);
10914 const len: usize = @intCast(ip.aggregateTypeLenIncludingSentinel(info.ty));
10915 break :b @sizeOf(Bytes) + len + @intFromBool(info.bytes.at(len - 1, ip) != 0);
10916 },
10917 .aggregate => b: {
10918 const info = extraData(extra_list, Tag.Aggregate, data);
10919 const fields_len: u32 = @intCast(ip.aggregateTypeLenIncludingSentinel(info.ty));
10920 break :b @sizeOf(Tag.Aggregate) + (@sizeOf(Index) * fields_len);
10921 },
10922 .repeated => @sizeOf(Repeated),
10923
10924 .float_f16 => 0,
10925 .float_f32 => 0,
10926 .float_f64 => @sizeOf(Float64),
10927 .float_f80 => @sizeOf(Float80),
10928 .float_f128 => @sizeOf(Float128),
10929 .float_c_longdouble_f80 => @sizeOf(Float80),
10930 .float_c_longdouble_f128 => @sizeOf(Float128),
10931 .float_comptime_float => @sizeOf(Float128),
10932 .@"extern" => @sizeOf(Tag.Extern),
10933 .func_decl => @sizeOf(Tag.FuncDecl),
10934 .func_instance => b: {
10935 const info = extraData(extra_list, Tag.FuncInstance, data);
10936 const ty = ip.typeOf(info.generic_owner);
10937 const params_len = ip.indexToKey(ty).func_type.param_types.len;
10938 break :b @sizeOf(Tag.FuncInstance) + @sizeOf(Index) * params_len;
10939 },
10940 .func_coerced => @sizeOf(Tag.FuncCoerced),
10941 .only_possible_value => 0,
10942 .union_value => @sizeOf(Key.Union),
10943 .bitpack => 2 * @sizeOf(u32),
10944
10945 .memoized_call => b: {
10946 const info = extraData(extra_list, MemoizedCall, data);
10947 break :b @sizeOf(MemoizedCall) + (@sizeOf(Index) * info.args_len);
10948 },
10949 });
10950 }
10951 }
10952 const SortContext = struct {
10953 map: *std.array_hash_map.Auto(Tag, TagStats),
10954 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
10955 const values = ctx.map.values();
10956 return values[a_index].bytes > values[b_index].bytes;
10957 //return values[a_index].count > values[b_index].count;
10958 }
10959 };
10960 counts.sort(SortContext{ .map = &counts });
10961 const len = @min(50, counts.count());
10962 try w.print(" top 50 tags:\n", .{});
10963 for (counts.keys()[0..len], counts.values()[0..len]) |tag, stats| {
10964 try w.print(" {t}: {d} occurrences, {d} total bytes\n", .{ tag, stats.count, stats.bytes });
10965 }
10966}
10967
10968fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
10969 for (ip.locals, 0..) |*local, tid| {
10970 // Early check for length 0, because `view()` is invalid if capacity is 0
10971 if (local.mutate.items.len == 0) continue;
10972 const items = local.shared.items.view();
10973 for (
10974 items.items(.tag)[0..local.mutate.items.len],
10975 items.items(.data)[0..local.mutate.items.len],
10976 0..,
10977 ) |tag, data, index| {
10978 const i = Index.Unwrapped.wrap(.{ .tid = @fromBackingInt(@intCast(tid)), .index = @intCast(index) }, ip);
10979 try w.print("${d} = {s}(", .{ i, @tagName(tag) });
10980 switch (tag) {
10981 .removed => {},
10982
10983 .simple_type => try w.print("{s}", .{@tagName(@as(SimpleType, @fromBackingInt(@intCast(@backingInt(i)))))}),
10984 .simple_value => try w.print("{s}", .{@tagName(@as(SimpleValue, @fromBackingInt(@intCast(@backingInt(i)))))}),
10985
10986 .type_int_signed,
10987 .type_int_unsigned,
10988 .type_array_small,
10989 .type_array_big,
10990 .type_vector,
10991 .type_pointer,
10992 .type_optional,
10993 .type_anyframe,
10994 .type_error_union,
10995 .type_anyerror_union,
10996 .type_error_set,
10997 .type_inferred_error_set,
10998 .type_tuple,
10999 .type_function,
11000 .type_struct,
11001 .type_struct_packed_auto,
11002 .type_struct_packed_explicit,
11003 .type_struct_packed_auto_defaults,
11004 .type_struct_packed_explicit_defaults,
11005 .type_union,
11006 .type_union_packed_auto,
11007 .type_union_packed_explicit,
11008 .type_enum_auto,
11009 .type_enum_explicit,
11010 .type_enum_nonexhaustive,
11011 .type_opaque,
11012 .type_spirv,
11013 .undef,
11014 .ptr_nav,
11015 .ptr_comptime_alloc,
11016 .ptr_uav,
11017 .ptr_uav_aligned,
11018 .ptr_comptime_field,
11019 .ptr_int,
11020 .ptr_eu_payload,
11021 .ptr_opt_payload,
11022 .ptr_elem,
11023 .ptr_field,
11024 .ptr_slice,
11025 .opt_payload,
11026 .int_u8,
11027 .int_u16,
11028 .int_u32,
11029 .int_i32,
11030 .int_usize,
11031 .int_comptime_int_u32,
11032 .int_comptime_int_i32,
11033 .int_small,
11034 .int_positive,
11035 .int_negative,
11036 .error_set_error,
11037 .error_union_error,
11038 .error_union_payload,
11039 .enum_literal,
11040 .enum_tag,
11041 .bytes,
11042 .aggregate,
11043 .repeated,
11044 .float_f16,
11045 .float_f32,
11046 .float_f64,
11047 .float_f80,
11048 .float_f128,
11049 .float_c_longdouble_f80,
11050 .float_c_longdouble_f128,
11051 .float_comptime_float,
11052 .@"extern",
11053 .func_decl,
11054 .func_instance,
11055 .func_coerced,
11056 .union_value,
11057 .bitpack,
11058 .memoized_call,
11059 => try w.print("{d}", .{data}),
11060
11061 .opt_null,
11062 .type_slice,
11063 .only_possible_value,
11064 => try w.print("${d}", .{data}),
11065 }
11066 try w.writeAll(")\n");
11067 }
11068 }
11069}
11070
11071pub fn dumpGenericInstances(ip: *const InternPool, allocator: Allocator) void {
11072 var buffer: [4096]u8 = undefined;
11073 const stderr = std.debug.lockStderr(&buffer);
11074 defer std.debug.unlockStderr();
11075 const w = &stderr.file_writer.interface;
11076 ip.dumpGenericInstancesFallible(allocator, w) catch return;
11077}
11078
11079pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator, w: *Io.Writer) !void {
11080 var arena_allocator = std.heap.ArenaAllocator.init(allocator);
11081 defer arena_allocator.deinit();
11082 const arena = arena_allocator.allocator();
11083
11084 var instances: std.array_hash_map.Auto(Index, std.ArrayList(Index)) = .empty;
11085 for (ip.locals, 0..) |*local, tid| {
11086 const items = local.shared.items.view().slice();
11087 const extra_list = local.shared.extra;
11088 for (
11089 items.items(.tag)[0..local.mutate.items.len],
11090 items.items(.data)[0..local.mutate.items.len],
11091 0..,
11092 ) |tag, data, index| {
11093 if (tag != .func_instance) continue;
11094 const info = extraData(extra_list, Tag.FuncInstance, data);
11095
11096 const gop = try instances.getOrPut(arena, info.generic_owner);
11097 if (!gop.found_existing) gop.value_ptr.* = .empty;
11098
11099 try gop.value_ptr.append(
11100 arena,
11101 Index.Unwrapped.wrap(.{ .tid = @fromBackingInt(@intCast(tid)), .index = @intCast(index) }, ip),
11102 );
11103 }
11104 }
11105
11106 const SortContext = struct {
11107 values: []std.ArrayList(Index),
11108 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
11109 return ctx.values[a_index].items.len > ctx.values[b_index].items.len;
11110 }
11111 };
11112
11113 instances.sort(SortContext{ .values = instances.values() });
11114 var it = instances.iterator();
11115 while (it.next()) |entry| {
11116 const generic_fn_owner_nav = ip.getNav(ip.funcDeclInfo(entry.key_ptr.*).owner_nav);
11117 try w.print("{f} ({d}): \n", .{ generic_fn_owner_nav.name.fmt(ip), entry.value_ptr.items.len });
11118 for (entry.value_ptr.items) |index| {
11119 const unwrapped_index = index.unwrap(ip);
11120 const func = ip.extraFuncInstance(unwrapped_index.tid, unwrapped_index.getExtra(ip), unwrapped_index.getData(ip));
11121 const owner_nav = ip.getNav(func.owner_nav);
11122 try w.print(" {f}: (", .{owner_nav.name.fmt(ip)});
11123 for (func.comptime_args.get(ip)) |arg| {
11124 if (arg != .none) {
11125 const key = ip.indexToKey(arg);
11126 try w.print(" {} ", .{key});
11127 }
11128 }
11129 try w.writeAll(")\n");
11130 }
11131 }
11132}
11133
11134pub fn getNav(ip: *const InternPool, index: Nav.Index) Nav {
11135 const unwrapped = index.unwrap(ip);
11136 const view = ip.getLocalShared(unwrapped.tid).navs.acquire().view();
11137 // We can't just call `view.get(unwrapped.index)`, because a concurrent call to `resolveNav`
11138 // could be writing to fields, making a non-atomic load illegal. Instead, atomically load
11139 // each field. We don't need any ordering guarantees because if we need to see (e.g.) the
11140 // resolved type of a `Nav`, that information should have already been released to our caller.
11141 const repr: Nav.Repr = .{
11142 // Load the first few fields non-atomically---they are never mutated after `Nav` creation.
11143 .name = view.items(.name)[unwrapped.index],
11144 .fqn = view.items(.fqn)[unwrapped.index],
11145 .analysis_namespace = view.items(.analysis_namespace)[unwrapped.index],
11146 .analysis_zir_index = view.items(.analysis_zir_index)[unwrapped.index],
11147 // The last few fields are populated by `resolveNav` so must be loaded atomically.
11148 .type = @atomicLoad(InternPool.Index, &view.items(.type)[unwrapped.index], .monotonic),
11149 .value = @atomicLoad(InternPool.Index, &view.items(.value)[unwrapped.index], .monotonic),
11150 .@"linksection" = @atomicLoad(OptionalNullTerminatedString, &view.items(.@"linksection")[unwrapped.index], .monotonic),
11151 .bits = @atomicLoad(Nav.Repr.Bits, &view.items(.bits)[unwrapped.index], .monotonic),
11152 };
11153 return repr.unpack();
11154}
11155
11156pub fn namespacePtr(ip: *InternPool, namespace_index: NamespaceIndex) *Zcu.Namespace {
11157 const unwrapped_namespace_index = namespace_index.unwrap(ip);
11158 const namespaces = ip.getLocalShared(unwrapped_namespace_index.tid).namespaces.acquire();
11159 const namespaces_bucket = namespaces.view().items(.@"0")[unwrapped_namespace_index.bucket_index];
11160 return &namespaces_bucket[unwrapped_namespace_index.index];
11161}
11162
11163/// Create a `ComptimeUnit`, forming an `AnalUnit` for a `comptime` declaration.
11164pub fn createComptimeUnit(
11165 ip: *InternPool,
11166 gpa: Allocator,
11167 io: Io,
11168 tid: Zcu.PerThread.Id,
11169 zir_index: TrackedInst.Index,
11170 namespace: NamespaceIndex,
11171) Allocator.Error!ComptimeUnit.Id {
11172 const comptime_units = ip.getLocal(tid).getMutableComptimeUnits(gpa, io);
11173 const id_unwrapped: ComptimeUnit.Id.Unwrapped = .{
11174 .tid = tid,
11175 .index = comptime_units.mutate.len,
11176 };
11177 try comptime_units.append(.{.{
11178 .zir_index = zir_index,
11179 .namespace = namespace,
11180 }});
11181 return id_unwrapped.wrap(ip);
11182}
11183
11184pub fn getComptimeUnit(ip: *const InternPool, id: ComptimeUnit.Id) ComptimeUnit {
11185 const unwrapped = id.unwrap(ip);
11186 const comptime_units = ip.getLocalShared(unwrapped.tid).comptime_units.acquire();
11187 return comptime_units.view().items(.@"0")[unwrapped.index];
11188}
11189
11190/// Create a `Nav` which does not undergo semantic analysis.
11191/// Since it is never analyzed, the `Nav`'s value must be known at creation time.
11192fn createNav(
11193 ip: *InternPool,
11194 gpa: Allocator,
11195 io: Io,
11196 tid: Zcu.PerThread.Id,
11197 name: NullTerminatedString,
11198 fqn: NullTerminatedString,
11199 resolved: @typeInfo(@FieldType(Nav, "resolved")).optional.child,
11200) Allocator.Error!Nav.Index {
11201 const navs = ip.getLocal(tid).getMutableNavs(gpa, io);
11202 const index_unwrapped: Nav.Index.Unwrapped = .{
11203 .tid = tid,
11204 .index = navs.mutate.len,
11205 };
11206 try navs.append(Nav.pack(.{
11207 .name = name,
11208 .fqn = fqn,
11209 .analysis = null,
11210 .resolved = resolved,
11211 }));
11212 return index_unwrapped.wrap(ip);
11213}
11214
11215/// Create a `Nav` which undergoes semantic analysis because it corresponds to a source declaration.
11216/// The value of the `Nav` is initially unresolved.
11217pub fn createDeclNav(
11218 ip: *InternPool,
11219 gpa: Allocator,
11220 io: Io,
11221 tid: Zcu.PerThread.Id,
11222 name: NullTerminatedString,
11223 fqn: NullTerminatedString,
11224 zir_index: TrackedInst.Index,
11225 namespace: NamespaceIndex,
11226) Allocator.Error!Nav.Index {
11227 const navs = ip.getLocal(tid).getMutableNavs(gpa, io);
11228
11229 try navs.ensureUnusedCapacity(1);
11230
11231 const nav = Nav.Index.Unwrapped.wrap(.{
11232 .tid = tid,
11233 .index = navs.mutate.len,
11234 }, ip);
11235
11236 navs.appendAssumeCapacity(Nav.pack(.{
11237 .name = name,
11238 .fqn = fqn,
11239 .analysis = .{
11240 .namespace = namespace,
11241 .zir_index = zir_index,
11242 .wanted = false,
11243 },
11244 .resolved = null,
11245 }));
11246
11247 return nav;
11248}
11249
11250/// Resolve the type (and possibly the value) of a `Nav` with an analysis owner.
11251/// If its status is already `resolved`, the old value is discarded.
11252pub fn resolveNav(
11253 ip: *InternPool,
11254 io: Io,
11255 nav: Nav.Index,
11256 resolved: @typeInfo(@FieldType(Nav, "resolved")).optional.child,
11257) void {
11258 const unwrapped = nav.unwrap(ip);
11259
11260 const local = ip.getLocal(unwrapped.tid);
11261 local.mutate.extra.mutex.lockUncancelable(io);
11262 defer local.mutate.extra.mutex.unlock(io);
11263
11264 const navs = local.shared.navs.view();
11265
11266 const nav_analysis_namespace = navs.items(.analysis_namespace);
11267 const nav_analysis_zir_index = navs.items(.analysis_zir_index);
11268 const nav_types = navs.items(.type);
11269 const nav_values = navs.items(.value);
11270 const nav_linksections = navs.items(.@"linksection");
11271 const nav_bits = navs.items(.bits);
11272
11273 assert(nav_analysis_namespace[unwrapped.index] != .none);
11274 assert(nav_analysis_zir_index[unwrapped.index] != .none);
11275
11276 @atomicStore(
11277 OptionalNullTerminatedString,
11278 &nav_linksections[unwrapped.index],
11279 resolved.@"linksection",
11280 .monotonic,
11281 );
11282
11283 const bits = &nav_bits[unwrapped.index];
11284 assert(@atomicLoad(Nav.Repr.Bits, bits, .monotonic).want_analysis); // otherwise we wouldn't be resolving `nav` at all
11285 @atomicStore(Nav.Repr.Bits, bits, .{
11286 .@"align" = resolved.@"align",
11287 .@"addrspace" = resolved.@"addrspace",
11288 .@"const" = resolved.@"const",
11289 .@"threadlocal" = resolved.@"threadlocal",
11290 .is_extern_decl = resolved.is_extern_decl,
11291 .want_analysis = true, // asserted above that this is already `true`
11292 }, .monotonic);
11293
11294 @atomicStore(
11295 InternPool.Index,
11296 &nav_types[unwrapped.index],
11297 resolved.type,
11298 .monotonic,
11299 );
11300
11301 @atomicStore(
11302 InternPool.Index,
11303 &nav_values[unwrapped.index],
11304 resolved.value,
11305 .monotonic,
11306 );
11307}
11308
11309pub fn createNamespace(
11310 ip: *InternPool,
11311 gpa: Allocator,
11312 io: Io,
11313 tid: Zcu.PerThread.Id,
11314 initialization: Zcu.Namespace,
11315) Allocator.Error!NamespaceIndex {
11316 const local = ip.getLocal(tid);
11317 const free_list_next = local.mutate.namespaces.free_list;
11318 if (free_list_next != Local.BucketListMutate.free_list_sentinel) {
11319 const reused_namespace_index: NamespaceIndex = @fromBackingInt(@intCast(free_list_next));
11320 const reused_namespace = ip.namespacePtr(reused_namespace_index);
11321 local.mutate.namespaces.free_list =
11322 @backingInt(@field(reused_namespace, Local.namespace_next_free_field));
11323 reused_namespace.* = initialization;
11324 return reused_namespace_index;
11325 }
11326 const namespaces = local.getMutableNamespaces(gpa, io);
11327 const last_bucket_len = local.mutate.namespaces.last_bucket_len & Local.namespaces_bucket_mask;
11328 if (last_bucket_len == 0) {
11329 try namespaces.ensureUnusedCapacity(1);
11330 var arena = namespaces.arena.promote(namespaces.gpa);
11331 defer namespaces.arena.* = arena.state;
11332 namespaces.appendAssumeCapacity(.{try arena.allocator().create(
11333 [1 << Local.namespaces_bucket_width]Zcu.Namespace,
11334 )});
11335 }
11336 const unwrapped_namespace_index: NamespaceIndex.Unwrapped = .{
11337 .tid = tid,
11338 .bucket_index = namespaces.mutate.len - 1,
11339 .index = last_bucket_len,
11340 };
11341 local.mutate.namespaces.last_bucket_len = last_bucket_len + 1;
11342 const namespace_index = unwrapped_namespace_index.wrap(ip);
11343 ip.namespacePtr(namespace_index).* = initialization;
11344 return namespace_index;
11345}
11346
11347pub fn destroyNamespace(
11348 ip: *InternPool,
11349 tid: Zcu.PerThread.Id,
11350 namespace_index: NamespaceIndex,
11351) void {
11352 const local = ip.getLocal(tid);
11353 const namespace = ip.namespacePtr(namespace_index);
11354 namespace.* = .{
11355 .parent = undefined,
11356 .file_scope = undefined,
11357 .owner_type = undefined,
11358 .generation = undefined,
11359 };
11360 @field(namespace, Local.namespace_next_free_field) =
11361 @fromBackingInt(@intCast(local.mutate.namespaces.free_list));
11362 local.mutate.namespaces.free_list = @backingInt(namespace_index);
11363}
11364
11365pub fn filePtr(ip: *const InternPool, file_index: FileIndex) *Zcu.File {
11366 const file_index_unwrapped = file_index.unwrap(ip);
11367 const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire();
11368 return files.view().items(.file)[file_index_unwrapped.index];
11369}
11370
11371pub fn createFile(
11372 ip: *InternPool,
11373 gpa: Allocator,
11374 io: Io,
11375 tid: Zcu.PerThread.Id,
11376 file: File,
11377) Allocator.Error!FileIndex {
11378 const files = ip.getLocal(tid).getMutableFiles(gpa, io);
11379 const file_index_unwrapped: FileIndex.Unwrapped = .{
11380 .tid = tid,
11381 .index = files.mutate.len,
11382 };
11383 try files.append(file);
11384 return file_index_unwrapped.wrap(ip);
11385}
11386
11387const EmbeddedNulls = enum {
11388 no_embedded_nulls,
11389 maybe_embedded_nulls,
11390
11391 fn StringType(comptime embedded_nulls: EmbeddedNulls) type {
11392 return switch (embedded_nulls) {
11393 .no_embedded_nulls => NullTerminatedString,
11394 .maybe_embedded_nulls => String,
11395 };
11396 }
11397
11398 fn OptionalStringType(comptime embedded_nulls: EmbeddedNulls) type {
11399 return switch (embedded_nulls) {
11400 .no_embedded_nulls => OptionalNullTerminatedString,
11401 .maybe_embedded_nulls => OptionalString,
11402 };
11403 }
11404};
11405
11406pub fn getOrPutString(
11407 ip: *InternPool,
11408 gpa: Allocator,
11409 io: Io,
11410 tid: Zcu.PerThread.Id,
11411 slice: []const u8,
11412 comptime embedded_nulls: EmbeddedNulls,
11413) Allocator.Error!embedded_nulls.StringType() {
11414 const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
11415 try string_bytes.ensureUnusedCapacity(slice.len + 1);
11416 string_bytes.appendSliceAssumeCapacity(.{slice});
11417 string_bytes.appendAssumeCapacity(.{0});
11418 return ip.getOrPutTrailingString(gpa, io, tid, @intCast(slice.len + 1), embedded_nulls);
11419}
11420
11421pub fn getOrPutStringFmt(
11422 ip: *InternPool,
11423 gpa: Allocator,
11424 io: Io,
11425 tid: Zcu.PerThread.Id,
11426 comptime format: []const u8,
11427 args: anytype,
11428 comptime embedded_nulls: EmbeddedNulls,
11429) Allocator.Error!embedded_nulls.StringType() {
11430 // ensure that references to strings in args do not get invalidated
11431 const format_z = format ++ .{0};
11432 const len: u32 = @intCast(std.fmt.count(format_z, args));
11433 const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
11434 const slice = try string_bytes.addManyAsSlice(len);
11435 assert((std.mem.print(slice[0], format_z, args) catch unreachable).len == len);
11436 return ip.getOrPutTrailingString(gpa, io, tid, len, embedded_nulls);
11437}
11438
11439pub fn getOrPutStringOpt(
11440 ip: *InternPool,
11441 gpa: Allocator,
11442 io: Io,
11443 tid: Zcu.PerThread.Id,
11444 slice: ?[]const u8,
11445 comptime embedded_nulls: EmbeddedNulls,
11446) Allocator.Error!embedded_nulls.OptionalStringType() {
11447 const string = try getOrPutString(ip, gpa, io, tid, slice orelse return .none, embedded_nulls);
11448 return string.toOptional();
11449}
11450
11451/// Uses the last len bytes of strings as the key.
11452pub fn getOrPutTrailingString(
11453 ip: *InternPool,
11454 gpa: Allocator,
11455 io: Io,
11456 tid: Zcu.PerThread.Id,
11457 len: u32,
11458 comptime embedded_nulls: EmbeddedNulls,
11459) Allocator.Error!embedded_nulls.StringType() {
11460 const local = ip.getLocal(tid);
11461 const strings = local.getMutableStrings(gpa, io);
11462 try strings.ensureUnusedCapacity(1);
11463 const string_bytes = local.getMutableStringBytes(gpa, io);
11464 const start: u32 = @intCast(string_bytes.mutate.len - len);
11465 if (len > 0 and string_bytes.view().items(.@"0")[string_bytes.mutate.len - 1] == 0) {
11466 string_bytes.mutate.len -= 1;
11467 } else {
11468 try string_bytes.ensureUnusedCapacity(1);
11469 }
11470 const key: []const u8 = string_bytes.view().items(.@"0")[start..];
11471 const value: embedded_nulls.StringType() = @fromBackingInt(@intCast(@backingInt((String.Unwrapped{
11472 .tid = tid,
11473 .index = strings.mutate.len - 1,
11474 }).wrap(ip))));
11475 const has_embedded_null = std.mem.findScalar(u8, key, 0) != null;
11476 switch (embedded_nulls) {
11477 .no_embedded_nulls => assert(!has_embedded_null),
11478 .maybe_embedded_nulls => if (has_embedded_null) {
11479 string_bytes.appendAssumeCapacity(.{0});
11480 strings.appendAssumeCapacity(.{string_bytes.mutate.len});
11481 return value;
11482 },
11483 }
11484
11485 const full_hash = Hash.hash(0, key);
11486 const hash: u32 = @truncate(full_hash >> 32);
11487 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
11488 var map = shard.shared.string_map.acquire();
11489 const Map = @TypeOf(map);
11490 var map_mask = map.header().mask();
11491 var map_index = hash;
11492 while (true) : (map_index += 1) {
11493 map_index &= map_mask;
11494 const entry = &map.entries[map_index];
11495 const index = entry.acquire().unwrap() orelse break;
11496 if (entry.hash != hash) continue;
11497 if (!index.eqlSlice(key, ip)) continue;
11498 string_bytes.shrinkRetainingCapacity(start);
11499 return @fromBackingInt(@intCast(@backingInt(index)));
11500 }
11501 shard.mutate.string_map.mutex.lock(io, tid);
11502 defer shard.mutate.string_map.mutex.unlock(io);
11503 if (map.entries != shard.shared.string_map.entries) {
11504 map = shard.shared.string_map;
11505 map_mask = map.header().mask();
11506 map_index = hash;
11507 }
11508 while (true) : (map_index += 1) {
11509 map_index &= map_mask;
11510 const entry = &map.entries[map_index];
11511 const index = entry.acquire().unwrap() orelse break;
11512 if (entry.hash != hash) continue;
11513 if (!index.eqlSlice(key, ip)) continue;
11514 string_bytes.shrinkRetainingCapacity(start);
11515 return @fromBackingInt(@intCast(@backingInt(index)));
11516 }
11517 defer shard.mutate.string_map.len += 1;
11518 const map_header = map.header().*;
11519 if (shard.mutate.string_map.len < map_header.capacity * 3 / 5) {
11520 string_bytes.appendAssumeCapacity(.{0});
11521 strings.appendAssumeCapacity(.{string_bytes.mutate.len});
11522 const entry = &map.entries[map_index];
11523 entry.hash = hash;
11524 entry.release(@fromBackingInt(@intCast(@backingInt(value))));
11525 return value;
11526 }
11527 const arena_state = &local.mutate.arena;
11528 var arena = arena_state.promote(gpa);
11529 defer arena_state.* = arena.state;
11530 const new_map_capacity = map_header.capacity * 2;
11531 const new_map_buf = try arena.allocator().alignedAlloc(
11532 u8,
11533 .fromByteUnits(Map.alignment),
11534 Map.entries_offset + new_map_capacity * @sizeOf(Map.Entry),
11535 );
11536 const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) };
11537 new_map.header().* = .{ .capacity = new_map_capacity };
11538 @memset(new_map.entries[0..new_map_capacity], .{ .value = .none, .hash = undefined });
11539 const new_map_mask = new_map.header().mask();
11540 map_index = 0;
11541 while (map_index < map_header.capacity) : (map_index += 1) {
11542 const entry = &map.entries[map_index];
11543 const index = entry.value.unwrap() orelse continue;
11544 const item_hash = entry.hash;
11545 var new_map_index = item_hash;
11546 while (true) : (new_map_index += 1) {
11547 new_map_index &= new_map_mask;
11548 const new_entry = &new_map.entries[new_map_index];
11549 if (new_entry.value != .none) continue;
11550 new_entry.* = .{
11551 .value = index.toOptional(),
11552 .hash = item_hash,
11553 };
11554 break;
11555 }
11556 }
11557 map = new_map;
11558 map_index = hash;
11559 while (true) : (map_index += 1) {
11560 map_index &= new_map_mask;
11561 if (map.entries[map_index].value == .none) break;
11562 }
11563 string_bytes.appendAssumeCapacity(.{0});
11564 strings.appendAssumeCapacity(.{string_bytes.mutate.len});
11565 map.entries[map_index] = .{
11566 .value = @fromBackingInt(@intCast(@backingInt(value))),
11567 .hash = hash,
11568 };
11569 shard.shared.string_map.release(new_map);
11570 return value;
11571}
11572
11573pub fn getString(ip: *InternPool, key: []const u8) OptionalNullTerminatedString {
11574 const full_hash = Hash.hash(0, key);
11575 const hash: u32 = @truncate(full_hash >> 32);
11576 const shard = &ip.shards[@intCast(full_hash & (ip.shards.len - 1))];
11577 const map = shard.shared.string_map.acquire();
11578 const map_mask = map.header().mask();
11579 var map_index = hash;
11580 while (true) : (map_index += 1) {
11581 map_index &= map_mask;
11582 const entry = &map.entries[map_index];
11583 const index = entry.value.unwrap() orelse return .none;
11584 if (entry.hash != hash) continue;
11585 if (index.eqlSlice(key, ip)) return index.toOptional();
11586 }
11587}
11588
11589pub fn typeOf(ip: *const InternPool, index: Index) Index {
11590 // This optimization of static keys is required so that typeOf can be called
11591 // on static keys that haven't been added yet during static key initialization.
11592 // An alternative would be to topological sort the static keys, but this would
11593 // mean that the range of type indices would not be dense.
11594 return switch (index) {
11595 .u0_type,
11596 .u1_type,
11597 .u8_type,
11598 .i8_type,
11599 .u16_type,
11600 .i16_type,
11601 .u29_type,
11602 .u32_type,
11603 .i32_type,
11604 .u64_type,
11605 .i64_type,
11606 .u80_type,
11607 .u128_type,
11608 .i128_type,
11609 .u256_type,
11610 .usize_type,
11611 .isize_type,
11612 .c_char_type,
11613 .c_short_type,
11614 .c_ushort_type,
11615 .c_int_type,
11616 .c_uint_type,
11617 .c_long_type,
11618 .c_ulong_type,
11619 .c_longlong_type,
11620 .c_ulonglong_type,
11621 .c_longdouble_type,
11622 .f16_type,
11623 .f32_type,
11624 .f64_type,
11625 .f80_type,
11626 .f128_type,
11627 .anyopaque_type,
11628 .bool_type,
11629 .void_type,
11630 .type_type,
11631 .anyerror_type,
11632 .comptime_int_type,
11633 .comptime_float_type,
11634 .noreturn_type,
11635 .anyframe_type,
11636 .null_type,
11637 .undefined_type,
11638 .enum_literal_type,
11639 .ptr_usize_type,
11640 .ptr_const_comptime_int_type,
11641 .manyptr_u8_type,
11642 .manyptr_const_u8_type,
11643 .manyptr_const_u8_sentinel_0_type,
11644 .manyptr_const_slice_const_u8_type,
11645 .slice_const_u8_type,
11646 .slice_const_u8_sentinel_0_type,
11647 .slice_const_slice_const_u8_type,
11648 .optional_type_type,
11649 .manyptr_const_type_type,
11650 .slice_const_type_type,
11651 .vector_8_i8_type,
11652 .vector_16_i8_type,
11653 .vector_32_i8_type,
11654 .vector_64_i8_type,
11655 .vector_1_u8_type,
11656 .vector_2_u8_type,
11657 .vector_4_u8_type,
11658 .vector_8_u8_type,
11659 .vector_16_u8_type,
11660 .vector_32_u8_type,
11661 .vector_64_u8_type,
11662 .vector_2_i16_type,
11663 .vector_4_i16_type,
11664 .vector_8_i16_type,
11665 .vector_16_i16_type,
11666 .vector_32_i16_type,
11667 .vector_4_u16_type,
11668 .vector_8_u16_type,
11669 .vector_16_u16_type,
11670 .vector_32_u16_type,
11671 .vector_2_i32_type,
11672 .vector_4_i32_type,
11673 .vector_8_i32_type,
11674 .vector_16_i32_type,
11675 .vector_4_u32_type,
11676 .vector_8_u32_type,
11677 .vector_16_u32_type,
11678 .vector_2_i64_type,
11679 .vector_4_i64_type,
11680 .vector_8_i64_type,
11681 .vector_2_u64_type,
11682 .vector_4_u64_type,
11683 .vector_8_u64_type,
11684 .vector_1_u128_type,
11685 .vector_2_u128_type,
11686 .vector_1_u256_type,
11687 .vector_4_f16_type,
11688 .vector_8_f16_type,
11689 .vector_16_f16_type,
11690 .vector_32_f16_type,
11691 .vector_2_f32_type,
11692 .vector_4_f32_type,
11693 .vector_8_f32_type,
11694 .vector_16_f32_type,
11695 .vector_2_f64_type,
11696 .vector_4_f64_type,
11697 .vector_8_f64_type,
11698 .optional_noreturn_type,
11699 .anyerror_void_error_union_type,
11700 .adhoc_inferred_error_set_type,
11701 .generic_poison_type,
11702 .empty_tuple_type,
11703 => .type_type,
11704
11705 .undef => .undefined_type,
11706 .zero, .one, .negative_one => .comptime_int_type,
11707 .undef_usize, .zero_usize, .one_usize => .usize_type,
11708 .undef_u1, .zero_u1, .one_u1 => .u1_type,
11709 .zero_u8, .one_u8, .four_u8 => .u8_type,
11710 .void_value => .void_type,
11711 .unreachable_value => .noreturn_type,
11712 .null_value => .null_type,
11713 .undef_bool, .bool_true, .bool_false => .bool_type,
11714 .empty_tuple => .empty_tuple_type,
11715
11716 // This optimization on tags is needed so that indexToKey can call
11717 // typeOf without being recursive.
11718 _ => {
11719 const unwrapped_index = index.unwrap(ip);
11720 const item = unwrapped_index.getItem(ip);
11721 return switch (item.tag) {
11722 .removed => unreachable,
11723
11724 .type_int_signed,
11725 .type_int_unsigned,
11726 .type_array_big,
11727 .type_array_small,
11728 .type_vector,
11729 .type_pointer,
11730 .type_slice,
11731 .type_optional,
11732 .type_anyframe,
11733 .type_error_union,
11734 .type_anyerror_union,
11735 .type_error_set,
11736 .type_inferred_error_set,
11737 .type_tuple,
11738 .type_function,
11739 .type_struct,
11740 .type_struct_packed_auto,
11741 .type_struct_packed_explicit,
11742 .type_struct_packed_auto_defaults,
11743 .type_struct_packed_explicit_defaults,
11744 .type_union,
11745 .type_union_packed_auto,
11746 .type_union_packed_explicit,
11747 .type_enum_auto,
11748 .type_enum_explicit,
11749 .type_enum_nonexhaustive,
11750 .type_opaque,
11751 .type_spirv,
11752 => .type_type,
11753
11754 .undef,
11755 .opt_null,
11756 .only_possible_value,
11757 => @fromBackingInt(@intCast(item.data)),
11758
11759 .simple_type, .simple_value => unreachable, // handled via Index above
11760
11761 inline .ptr_nav,
11762 .ptr_comptime_alloc,
11763 .ptr_uav,
11764 .ptr_uav_aligned,
11765 .ptr_comptime_field,
11766 .ptr_int,
11767 .ptr_eu_payload,
11768 .ptr_opt_payload,
11769 .ptr_elem,
11770 .ptr_field,
11771 .ptr_slice,
11772 .opt_payload,
11773 .error_union_payload,
11774 .int_small,
11775 .error_set_error,
11776 .error_union_error,
11777 .enum_tag,
11778 .@"extern",
11779 .func_decl,
11780 .func_instance,
11781 .func_coerced,
11782 .union_value,
11783 .bytes,
11784 .aggregate,
11785 .repeated,
11786 .bitpack,
11787 => |t| {
11788 const extra_list = unwrapped_index.getExtra(ip);
11789 return @fromBackingInt(@intCast(extra_list.view().items(.@"0")[item.data + std.meta.fieldIndex(t.Payload(), "ty").?]));
11790 },
11791
11792 .int_u8 => .u8_type,
11793 .int_u16 => .u16_type,
11794 .int_u32 => .u32_type,
11795 .int_i32 => .i32_type,
11796 .int_usize => .usize_type,
11797
11798 .int_comptime_int_u32,
11799 .int_comptime_int_i32,
11800 => .comptime_int_type,
11801
11802 // Note these are stored in limbs data, not extra data.
11803 .int_positive,
11804 .int_negative,
11805 => {
11806 const limbs_list = ip.getLocalShared(unwrapped_index.tid).getLimbs();
11807 const int: Int = @bitCast(limbs_list.view().items(.@"0")[item.data..][0..Int.limbs_items_len].*);
11808 return int.ty;
11809 },
11810
11811 .enum_literal => .enum_literal_type,
11812 .float_f16 => .f16_type,
11813 .float_f32 => .f32_type,
11814 .float_f64 => .f64_type,
11815 .float_f80 => .f80_type,
11816 .float_f128 => .f128_type,
11817
11818 .float_c_longdouble_f80,
11819 .float_c_longdouble_f128,
11820 => .c_longdouble_type,
11821
11822 .float_comptime_float => .comptime_float_type,
11823
11824 .memoized_call => unreachable,
11825 };
11826 },
11827
11828 .none => unreachable,
11829 };
11830}
11831
11832/// Assumes that the enum's field indexes equal its value tags.
11833pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {
11834 const int = ip.indexToKey(i).enum_tag.int;
11835 return @fromBackingInt(@intCast(ip.indexToKey(int).int.storage.u64));
11836}
11837
11838pub fn toFunc(ip: *const InternPool, i: Index) Key.Func {
11839 return ip.indexToKey(i).func;
11840}
11841
11842pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {
11843 return switch (ip.indexToKey(ty)) {
11844 .struct_type => ip.loadStructType(ty).field_types.len,
11845 .tuple_type => |tuple_type| tuple_type.types.len,
11846 .array_type => |array_type| array_type.len,
11847 .vector_type => |vector_type| vector_type.len,
11848 else => unreachable,
11849 };
11850}
11851
11852pub fn aggregateTypeLenIncludingSentinel(ip: *const InternPool, ty: Index) u64 {
11853 return switch (ip.indexToKey(ty)) {
11854 .struct_type => ip.loadStructType(ty).field_types.len,
11855 .tuple_type => |tuple_type| tuple_type.types.len,
11856 .array_type => |array_type| array_type.lenIncludingSentinel(),
11857 .vector_type => |vector_type| vector_type.len,
11858 else => unreachable,
11859 };
11860}
11861
11862pub fn funcTypeReturnType(ip: *const InternPool, ty: Index) Index {
11863 const unwrapped_ty = ty.unwrap(ip);
11864 const ty_extra = unwrapped_ty.getExtra(ip);
11865 const ty_item = unwrapped_ty.getItem(ip);
11866 const child_extra, const child_item = switch (ty_item.tag) {
11867 .type_pointer => child: {
11868 const child_index: Index = @fromBackingInt(@intCast(ty_extra.view().items(.@"0")[
11869 ty_item.data + std.meta.fieldIndex(Tag.TypePointer, "child").?
11870 ]));
11871 const unwrapped_child = child_index.unwrap(ip);
11872 break :child .{ unwrapped_child.getExtra(ip), unwrapped_child.getItem(ip) };
11873 },
11874 .type_function => .{ ty_extra, ty_item },
11875 else => unreachable,
11876 };
11877 assert(child_item.tag == .type_function);
11878 return @fromBackingInt(@intCast(child_extra.view().items(.@"0")[
11879 child_item.data + std.meta.fieldIndex(Tag.TypeFunction, "return_type").?
11880 ]));
11881}
11882
11883pub fn isUndef(ip: *const InternPool, val: Index) bool {
11884 return val.unwrap(ip).getTag(ip) == .undef;
11885}
11886
11887pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.BaseAddr.Tag {
11888 var base = val;
11889 while (true) {
11890 const unwrapped_base = base.unwrap(ip);
11891 const base_item = unwrapped_base.getItem(ip);
11892 switch (base_item.tag) {
11893 .ptr_nav => return .nav,
11894 .ptr_comptime_alloc => return .comptime_alloc,
11895 .ptr_uav,
11896 .ptr_uav_aligned,
11897 => return .uav,
11898 .ptr_comptime_field => return .comptime_field,
11899 .ptr_int => return .int,
11900 inline .ptr_eu_payload,
11901 .ptr_opt_payload,
11902 .ptr_elem,
11903 .ptr_field,
11904 => |tag| base = @fromBackingInt(@intCast(unwrapped_base.getExtra(ip).view().items(.@"0")[
11905 base_item.data + std.meta.fieldIndex(tag.Payload(), "base").?
11906 ])),
11907 inline .ptr_slice => |tag| base = @fromBackingInt(@intCast(unwrapped_base.getExtra(ip).view().items(.@"0")[
11908 base_item.data + std.meta.fieldIndex(tag.Payload(), "ptr").?
11909 ])),
11910 else => return null,
11911 }
11912 }
11913}
11914
11915/// This is a particularly hot function, so we operate directly on encodings
11916/// rather than the more straightforward implementation of calling `indexToKey`.
11917/// Asserts `index` is not `.generic_poison_type`.
11918pub fn zigTypeTag(ip: *const InternPool, index: Index) std.lang.TypeId {
11919 return switch (index) {
11920 .u0_type,
11921 .u1_type,
11922 .u8_type,
11923 .i8_type,
11924 .u16_type,
11925 .i16_type,
11926 .u29_type,
11927 .u32_type,
11928 .i32_type,
11929 .u64_type,
11930 .i64_type,
11931 .u80_type,
11932 .u128_type,
11933 .i128_type,
11934 .u256_type,
11935 .usize_type,
11936 .isize_type,
11937 .c_char_type,
11938 .c_short_type,
11939 .c_ushort_type,
11940 .c_int_type,
11941 .c_uint_type,
11942 .c_long_type,
11943 .c_ulong_type,
11944 .c_longlong_type,
11945 .c_ulonglong_type,
11946 => .int,
11947
11948 .c_longdouble_type,
11949 .f16_type,
11950 .f32_type,
11951 .f64_type,
11952 .f80_type,
11953 .f128_type,
11954 => .float,
11955
11956 .anyopaque_type => .@"opaque",
11957 .bool_type => .bool,
11958 .void_type => .void,
11959 .type_type => .type,
11960 .anyerror_type, .adhoc_inferred_error_set_type => .error_set,
11961 .comptime_int_type => .comptime_int,
11962 .comptime_float_type => .comptime_float,
11963 .noreturn_type => .noreturn,
11964 .anyframe_type => .@"anyframe",
11965 .null_type => .null,
11966 .undefined_type => .undefined,
11967 .enum_literal_type => .enum_literal,
11968
11969 .ptr_usize_type,
11970 .ptr_const_comptime_int_type,
11971 .manyptr_u8_type,
11972 .manyptr_const_u8_type,
11973 .manyptr_const_u8_sentinel_0_type,
11974 .manyptr_const_slice_const_u8_type,
11975 .slice_const_u8_type,
11976 .slice_const_u8_sentinel_0_type,
11977 .slice_const_slice_const_u8_type,
11978 .manyptr_const_type_type,
11979 .slice_const_type_type,
11980 => .pointer,
11981
11982 .vector_8_i8_type,
11983 .vector_16_i8_type,
11984 .vector_32_i8_type,
11985 .vector_64_i8_type,
11986 .vector_1_u8_type,
11987 .vector_2_u8_type,
11988 .vector_4_u8_type,
11989 .vector_8_u8_type,
11990 .vector_16_u8_type,
11991 .vector_32_u8_type,
11992 .vector_64_u8_type,
11993 .vector_2_i16_type,
11994 .vector_4_i16_type,
11995 .vector_8_i16_type,
11996 .vector_16_i16_type,
11997 .vector_32_i16_type,
11998 .vector_4_u16_type,
11999 .vector_8_u16_type,
12000 .vector_16_u16_type,
12001 .vector_32_u16_type,
12002 .vector_2_i32_type,
12003 .vector_4_i32_type,
12004 .vector_8_i32_type,
12005 .vector_16_i32_type,
12006 .vector_4_u32_type,
12007 .vector_8_u32_type,
12008 .vector_16_u32_type,
12009 .vector_2_i64_type,
12010 .vector_4_i64_type,
12011 .vector_8_i64_type,
12012 .vector_2_u64_type,
12013 .vector_4_u64_type,
12014 .vector_8_u64_type,
12015 .vector_1_u128_type,
12016 .vector_2_u128_type,
12017 .vector_1_u256_type,
12018 .vector_4_f16_type,
12019 .vector_8_f16_type,
12020 .vector_16_f16_type,
12021 .vector_32_f16_type,
12022 .vector_2_f32_type,
12023 .vector_4_f32_type,
12024 .vector_8_f32_type,
12025 .vector_16_f32_type,
12026 .vector_2_f64_type,
12027 .vector_4_f64_type,
12028 .vector_8_f64_type,
12029 => .vector,
12030
12031 .optional_type_type => .optional,
12032 .optional_noreturn_type => .optional,
12033 .anyerror_void_error_union_type => .error_union,
12034 .empty_tuple_type => .@"struct",
12035
12036 .generic_poison_type => unreachable,
12037
12038 // values, not types
12039 .undef => unreachable,
12040 .undef_bool => unreachable,
12041 .undef_usize => unreachable,
12042 .undef_u1 => unreachable,
12043 .zero => unreachable,
12044 .zero_usize => unreachable,
12045 .zero_u1 => unreachable,
12046 .zero_u8 => unreachable,
12047 .one => unreachable,
12048 .one_usize => unreachable,
12049 .one_u1 => unreachable,
12050 .one_u8 => unreachable,
12051 .four_u8 => unreachable,
12052 .negative_one => unreachable,
12053 .void_value => unreachable,
12054 .unreachable_value => unreachable,
12055 .null_value => unreachable,
12056 .bool_true => unreachable,
12057 .bool_false => unreachable,
12058 .empty_tuple => unreachable,
12059
12060 _ => switch (index.unwrap(ip).getTag(ip)) {
12061 .removed => unreachable,
12062
12063 .type_int_signed,
12064 .type_int_unsigned,
12065 => .int,
12066
12067 .type_array_big,
12068 .type_array_small,
12069 => .array,
12070
12071 .type_vector => .vector,
12072
12073 .type_pointer,
12074 .type_slice,
12075 => .pointer,
12076
12077 .type_optional => .optional,
12078 .type_anyframe => .@"anyframe",
12079
12080 .type_error_union,
12081 .type_anyerror_union,
12082 => .error_union,
12083
12084 .type_error_set,
12085 .type_inferred_error_set,
12086 => .error_set,
12087
12088 .simple_type => unreachable, // handled via Index tag above
12089
12090 .type_tuple => .@"struct",
12091
12092 .type_struct,
12093 .type_struct_packed_auto,
12094 .type_struct_packed_explicit,
12095 .type_struct_packed_auto_defaults,
12096 .type_struct_packed_explicit_defaults,
12097 => .@"struct",
12098 .type_union,
12099 .type_union_packed_auto,
12100 .type_union_packed_explicit,
12101 => .@"union",
12102 .type_enum_auto,
12103 .type_enum_explicit,
12104 .type_enum_nonexhaustive,
12105 => .@"enum",
12106 .type_opaque,
12107 => .@"opaque",
12108
12109 .type_spirv => .spirv,
12110
12111 .type_function => .@"fn",
12112
12113 // values, not types
12114 .undef,
12115 .simple_value,
12116 .ptr_nav,
12117 .ptr_comptime_alloc,
12118 .ptr_uav,
12119 .ptr_uav_aligned,
12120 .ptr_comptime_field,
12121 .ptr_int,
12122 .ptr_eu_payload,
12123 .ptr_opt_payload,
12124 .ptr_elem,
12125 .ptr_field,
12126 .ptr_slice,
12127 .opt_payload,
12128 .opt_null,
12129 .int_u8,
12130 .int_u16,
12131 .int_u32,
12132 .int_i32,
12133 .int_usize,
12134 .int_comptime_int_u32,
12135 .int_comptime_int_i32,
12136 .int_small,
12137 .int_positive,
12138 .int_negative,
12139 .error_set_error,
12140 .error_union_error,
12141 .error_union_payload,
12142 .enum_literal,
12143 .enum_tag,
12144 .float_f16,
12145 .float_f32,
12146 .float_f64,
12147 .float_f80,
12148 .float_f128,
12149 .float_c_longdouble_f80,
12150 .float_c_longdouble_f128,
12151 .float_comptime_float,
12152 .@"extern",
12153 .func_decl,
12154 .func_instance,
12155 .func_coerced,
12156 .only_possible_value,
12157 .union_value,
12158 .bytes,
12159 .aggregate,
12160 .repeated,
12161 .bitpack,
12162 // memoization, not types
12163 .memoized_call,
12164 => unreachable,
12165 },
12166 .none => unreachable, // special tag
12167 };
12168}
12169
12170pub fn isFuncBody(ip: *const InternPool, func: Index) bool {
12171 return switch (func.unwrap(ip).getTag(ip)) {
12172 .func_decl, .func_instance, .func_coerced => true,
12173 else => false,
12174 };
12175}
12176
12177fn funcAnalysisPtr(ip: *const InternPool, func: Index) *FuncAnalysis {
12178 const unwrapped_func = func.unwrap(ip);
12179 const extra = unwrapped_func.getExtra(ip);
12180 const item = unwrapped_func.getItem(ip);
12181 const extra_index = switch (item.tag) {
12182 .func_decl => item.data + std.meta.fieldIndex(Tag.FuncDecl, "analysis").?,
12183 .func_instance => item.data + std.meta.fieldIndex(Tag.FuncInstance, "analysis").?,
12184 .func_coerced => {
12185 const extra_index = item.data + std.meta.fieldIndex(Tag.FuncCoerced, "func").?;
12186 const coerced_func_index: Index = @fromBackingInt(@intCast(extra.view().items(.@"0")[extra_index]));
12187 const unwrapped_coerced_func = coerced_func_index.unwrap(ip);
12188 const coerced_func_item = unwrapped_coerced_func.getItem(ip);
12189 return @ptrCast(&unwrapped_coerced_func.getExtra(ip).view().items(.@"0")[
12190 switch (coerced_func_item.tag) {
12191 .func_decl => coerced_func_item.data + std.meta.fieldIndex(Tag.FuncDecl, "analysis").?,
12192 .func_instance => coerced_func_item.data + std.meta.fieldIndex(Tag.FuncInstance, "analysis").?,
12193 else => unreachable,
12194 }
12195 ]);
12196 },
12197 else => unreachable,
12198 };
12199 return @ptrCast(&extra.view().items(.@"0")[extra_index]);
12200}
12201
12202pub fn funcAnalysisUnordered(ip: *const InternPool, func: Index) FuncAnalysis {
12203 return @atomicLoad(FuncAnalysis, ip.funcAnalysisPtr(func), .unordered);
12204}
12205
12206pub fn funcSetHasErrorTrace(ip: *InternPool, io: Io, func: Index, has_error_trace: bool) void {
12207 const unwrapped_func = func.unwrap(ip);
12208 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
12209 extra_mutex.lockUncancelable(io);
12210 defer extra_mutex.unlock(io);
12211
12212 const analysis_ptr = ip.funcAnalysisPtr(func);
12213 var analysis = analysis_ptr.*;
12214 analysis.has_error_trace = has_error_trace;
12215 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
12216}
12217
12218pub fn funcSetDisableInstrumentation(ip: *InternPool, io: Io, func: Index) void {
12219 const unwrapped_func = func.unwrap(ip);
12220 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
12221 extra_mutex.lockUncancelable(io);
12222 defer extra_mutex.unlock(io);
12223
12224 const analysis_ptr = ip.funcAnalysisPtr(func);
12225 var analysis = analysis_ptr.*;
12226 analysis.disable_instrumentation = true;
12227 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
12228}
12229
12230pub fn funcSetDisableIntrinsics(ip: *InternPool, io: Io, func: Index) void {
12231 const unwrapped_func = func.unwrap(ip);
12232 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
12233 extra_mutex.lockUncancelable(io);
12234 defer extra_mutex.unlock(io);
12235
12236 const analysis_ptr = ip.funcAnalysisPtr(func);
12237 var analysis = analysis_ptr.*;
12238 analysis.disable_intrinsics = true;
12239 @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
12240}
12241
12242pub fn funcZirBodyInst(ip: *const InternPool, func: Index) TrackedInst.Index {
12243 const unwrapped_func = func.unwrap(ip);
12244 const item = unwrapped_func.getItem(ip);
12245 const item_extra = unwrapped_func.getExtra(ip);
12246 const zir_body_inst_field_index = std.meta.fieldIndex(Tag.FuncDecl, "zir_body_inst").?;
12247 switch (item.tag) {
12248 .func_decl => return @fromBackingInt(@intCast(item_extra.view().items(.@"0")[item.data + zir_body_inst_field_index])),
12249 .func_instance => {
12250 const generic_owner_field_index = std.meta.fieldIndex(Tag.FuncInstance, "generic_owner").?;
12251 const func_decl_index: Index = @fromBackingInt(@intCast(item_extra.view().items(.@"0")[item.data + generic_owner_field_index]));
12252 const unwrapped_func_decl = func_decl_index.unwrap(ip);
12253 const func_decl_item = unwrapped_func_decl.getItem(ip);
12254 const func_decl_extra = unwrapped_func_decl.getExtra(ip);
12255 assert(func_decl_item.tag == .func_decl);
12256 return @fromBackingInt(@intCast(func_decl_extra.view().items(.@"0")[func_decl_item.data + zir_body_inst_field_index]));
12257 },
12258 .func_coerced => {
12259 const uncoerced_func_index: Index = @fromBackingInt(@intCast(item_extra.view().items(.@"0")[
12260 item.data + std.meta.fieldIndex(Tag.FuncCoerced, "func").?
12261 ]));
12262 return ip.funcZirBodyInst(uncoerced_func_index);
12263 },
12264 else => unreachable,
12265 }
12266}
12267
12268pub fn iesFuncIndex(ip: *const InternPool, ies_index: Index) Index {
12269 const item = ies_index.unwrap(ip).getItem(ip);
12270 assert(item.tag == .type_inferred_error_set);
12271 const func_index: Index = @fromBackingInt(@intCast(item.data));
12272 switch (func_index.unwrap(ip).getTag(ip)) {
12273 .func_decl, .func_instance => {},
12274 else => unreachable, // assertion failed
12275 }
12276 return func_index;
12277}
12278
12279/// Returns a mutable pointer to the resolved error set type of an inferred
12280/// error set function. The returned pointer is invalidated when anything is
12281/// added to `ip`.
12282fn funcIesResolvedPtr(ip: *const InternPool, func_index: Index) *Index {
12283 assert(ip.funcAnalysisUnordered(func_index).inferred_error_set);
12284 const unwrapped_func = func_index.unwrap(ip);
12285 const func_extra = unwrapped_func.getExtra(ip);
12286 const func_item = unwrapped_func.getItem(ip);
12287 const extra_index = switch (func_item.tag) {
12288 .func_decl => func_item.data + @typeInfo(Tag.FuncDecl).@"struct".field_names.len,
12289 .func_instance => func_item.data + @typeInfo(Tag.FuncInstance).@"struct".field_names.len,
12290 .func_coerced => {
12291 const uncoerced_func_index: Index = @fromBackingInt(@intCast(func_extra.view().items(.@"0")[
12292 func_item.data + std.meta.fieldIndex(Tag.FuncCoerced, "func").?
12293 ]));
12294 const unwrapped_uncoerced_func = uncoerced_func_index.unwrap(ip);
12295 const uncoerced_func_item = unwrapped_uncoerced_func.getItem(ip);
12296 return @ptrCast(&unwrapped_uncoerced_func.getExtra(ip).view().items(.@"0")[
12297 switch (uncoerced_func_item.tag) {
12298 .func_decl => uncoerced_func_item.data + @typeInfo(Tag.FuncDecl).@"struct".field_names.len,
12299 .func_instance => uncoerced_func_item.data + @typeInfo(Tag.FuncInstance).@"struct".field_names.len,
12300 else => unreachable,
12301 }
12302 ]);
12303 },
12304 else => unreachable,
12305 };
12306 return @ptrCast(&func_extra.view().items(.@"0")[extra_index]);
12307}
12308
12309pub fn funcIesResolvedUnordered(ip: *const InternPool, index: Index) Index {
12310 return @atomicLoad(Index, ip.funcIesResolvedPtr(index), .unordered);
12311}
12312
12313pub fn funcSetIesResolved(ip: *InternPool, io: Io, index: Index, ies: Index) void {
12314 const unwrapped_func = index.unwrap(ip);
12315 const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
12316 extra_mutex.lockUncancelable(io);
12317 defer extra_mutex.unlock(io);
12318
12319 @atomicStore(Index, ip.funcIesResolvedPtr(index), ies, .release);
12320}
12321
12322pub fn funcDeclInfo(ip: *const InternPool, index: Index) Key.Func {
12323 const unwrapped_index = index.unwrap(ip);
12324 const item = unwrapped_index.getItem(ip);
12325 assert(item.tag == .func_decl);
12326 return extraFuncDecl(unwrapped_index.tid, unwrapped_index.getExtra(ip), item.data);
12327}
12328
12329pub fn funcTypeParamsLen(ip: *const InternPool, index: Index) u32 {
12330 const unwrapped_index = index.unwrap(ip);
12331 const extra_list = unwrapped_index.getExtra(ip);
12332 const item = unwrapped_index.getItem(ip);
12333 assert(item.tag == .type_function);
12334 return extra_list.view().items(.@"0")[item.data + std.meta.fieldIndex(Tag.TypeFunction, "params_len").?];
12335}
12336
12337pub fn unwrapCoercedFunc(ip: *const InternPool, index: Index) Index {
12338 const unwrapped_index = index.unwrap(ip);
12339 const item = unwrapped_index.getItem(ip);
12340 return switch (item.tag) {
12341 .func_coerced => @fromBackingInt(@intCast(unwrapped_index.getExtra(ip).view().items(.@"0")[
12342 item.data + std.meta.fieldIndex(Tag.FuncCoerced, "func").?
12343 ])),
12344 .func_instance, .func_decl => index,
12345 else => unreachable,
12346 };
12347}
12348
12349/// Puts `name` into `names_slice` at the next index (that being the current length of `map`).
12350/// Also inserts the name into `map`. If there is an existing field with this name, its index
12351/// is returned. Otherwise, `null` is returned.
12352pub fn addFieldName(
12353 ip: *InternPool,
12354 names: NullTerminatedString.Slice,
12355 map: MapIndex,
12356 name: NullTerminatedString,
12357) ?u32 {
12358 const m = map.get(ip);
12359 const field_idx = m.count();
12360 const names_slice = names.get(ip);
12361 names_slice[field_idx] = name;
12362 const adapter: NullTerminatedString.Adapter = .{ .strings = names_slice[0..field_idx] };
12363 const gop = m.getOrPutAssumeCapacityAdapted(name, adapter);
12364 if (gop.found_existing) return @intCast(gop.index);
12365 assert(gop.index == field_idx);
12366 return null;
12367}
12368
12369/// Like `addFieldName`, but instead of adding a field name to a struct, union, or enum, adds a
12370/// field tag value for an enum.
12371pub fn addFieldTagValue(
12372 ip: *InternPool,
12373 values: Index.Slice,
12374 map: MapIndex,
12375 value: Index,
12376) ?u32 {
12377 const m = map.get(ip);
12378 const field_idx = m.count();
12379 const values_slice = values.get(ip);
12380 values_slice[field_idx] = value;
12381 const adapter: Index.Adapter = .{ .indexes = values_slice[0..field_idx] };
12382 const gop = m.getOrPutAssumeCapacityAdapted(value, adapter);
12383 if (gop.found_existing) return @intCast(gop.index);
12384 assert(gop.index == field_idx);
12385 return null;
12386}
12387
12388/// Used only by `get` for pointer values, and mainly intended to use `Tag.ptr_uav`
12389/// encoding instead of `Tag.ptr_uav_aligned` when possible.
12390fn ptrsHaveSameAlignment(ip: *InternPool, a_ty: Index, a_info: Key.PtrType, b_ty: Index) bool {
12391 if (a_ty == b_ty) return true;
12392 const b_info = ip.indexToKey(b_ty).ptr_type;
12393 return a_info.flags.alignment == b_info.flags.alignment and
12394 (a_info.child == b_info.child or a_info.flags.alignment != .none);
12395}
12396
12397const GlobalErrorSet = struct {
12398 shared: struct {
12399 names: Names,
12400 map: Shard.Map(GlobalErrorSet.Index),
12401 } align(std.atomic.cache_line),
12402 mutate: struct {
12403 names: Local.ListMutate,
12404 map: struct { mutex: Io.Mutex },
12405 } align(std.atomic.cache_line),
12406
12407 const Names = Local.List(struct { NullTerminatedString });
12408
12409 const empty: GlobalErrorSet = .{
12410 .shared = .{
12411 .names = .empty,
12412 .map = .empty,
12413 },
12414 .mutate = .{
12415 .names = .empty,
12416 .map = .{ .mutex = .init },
12417 },
12418 };
12419
12420 const Index = enum(Zcu.ErrorInt) {
12421 none = 0,
12422 _,
12423 };
12424
12425 /// Not thread-safe, may only be called from the main thread.
12426 pub fn getNamesFromMainThread(ges: *const GlobalErrorSet) []const NullTerminatedString {
12427 const len = ges.mutate.names.len;
12428 return if (len > 0) ges.shared.names.view().items(.@"0")[0..len] else &.{};
12429 }
12430
12431 fn getErrorValue(
12432 ges: *GlobalErrorSet,
12433 gpa: Allocator,
12434 io: Io,
12435 arena_state: *std.heap.ArenaAllocator.State,
12436 name: NullTerminatedString,
12437 ) Allocator.Error!GlobalErrorSet.Index {
12438 if (name == .empty) return .none;
12439 const hash = std.hash.int(@backingInt(name));
12440 var map = ges.shared.map.acquire();
12441 const Map = @TypeOf(map);
12442 var map_mask = map.header().mask();
12443 const names = ges.shared.names.acquire();
12444 var map_index = hash;
12445 while (true) : (map_index += 1) {
12446 map_index &= map_mask;
12447 const entry = &map.entries[map_index];
12448 const index = entry.acquire();
12449 if (index == .none) break;
12450 if (entry.hash != hash) continue;
12451 if (names.view().items(.@"0")[@backingInt(index) - 1] == name) return index;
12452 }
12453 ges.mutate.map.mutex.lockUncancelable(io);
12454 defer ges.mutate.map.mutex.unlock(io);
12455 if (map.entries != ges.shared.map.entries) {
12456 map = ges.shared.map;
12457 map_mask = map.header().mask();
12458 map_index = hash;
12459 }
12460 while (true) : (map_index += 1) {
12461 map_index &= map_mask;
12462 const entry = &map.entries[map_index];
12463 const index = entry.value;
12464 if (index == .none) break;
12465 if (entry.hash != hash) continue;
12466 if (names.view().items(.@"0")[@backingInt(index) - 1] == name) return index;
12467 }
12468 const mutable_names: Names.Mutable = .{
12469 .gpa = gpa,
12470 .io = io,
12471 .arena = arena_state,
12472 .mutate = &ges.mutate.names,
12473 .list = &ges.shared.names,
12474 };
12475 try mutable_names.ensureUnusedCapacity(1);
12476 const map_header = map.header().*;
12477 if (ges.mutate.names.len < map_header.capacity * 3 / 5) {
12478 mutable_names.appendAssumeCapacity(.{name});
12479 const index: GlobalErrorSet.Index = @fromBackingInt(@intCast(mutable_names.mutate.len));
12480 const entry = &map.entries[map_index];
12481 entry.hash = hash;
12482 entry.release(index);
12483 return index;
12484 }
12485 var arena = arena_state.promote(gpa);
12486 defer arena_state.* = arena.state;
12487 const new_map_capacity = map_header.capacity * 2;
12488 const new_map_buf = try arena.allocator().alignedAlloc(
12489 u8,
12490 .fromByteUnits(Map.alignment),
12491 Map.entries_offset + new_map_capacity * @sizeOf(Map.Entry),
12492 );
12493 const new_map: Map = .{ .entries = @ptrCast(new_map_buf[Map.entries_offset..].ptr) };
12494 new_map.header().* = .{ .capacity = new_map_capacity };
12495 @memset(new_map.entries[0..new_map_capacity], .{ .value = .none, .hash = undefined });
12496 const new_map_mask = new_map.header().mask();
12497 map_index = 0;
12498 while (map_index < map_header.capacity) : (map_index += 1) {
12499 const entry = &map.entries[map_index];
12500 const index = entry.value;
12501 if (index == .none) continue;
12502 const item_hash = entry.hash;
12503 var new_map_index = item_hash;
12504 while (true) : (new_map_index += 1) {
12505 new_map_index &= new_map_mask;
12506 const new_entry = &new_map.entries[new_map_index];
12507 if (new_entry.value != .none) continue;
12508 new_entry.* = .{
12509 .value = index,
12510 .hash = item_hash,
12511 };
12512 break;
12513 }
12514 }
12515 map = new_map;
12516 map_index = hash;
12517 while (true) : (map_index += 1) {
12518 map_index &= new_map_mask;
12519 if (map.entries[map_index].value == .none) break;
12520 }
12521 mutable_names.appendAssumeCapacity(.{name});
12522 const index: GlobalErrorSet.Index = @fromBackingInt(@intCast(mutable_names.mutate.len));
12523 map.entries[map_index] = .{ .value = index, .hash = hash };
12524 ges.shared.map.release(new_map);
12525 return index;
12526 }
12527
12528 fn getErrorValueIfExists(
12529 ges: *const GlobalErrorSet,
12530 name: NullTerminatedString,
12531 ) ?GlobalErrorSet.Index {
12532 if (name == .empty) return .none;
12533 const hash = std.hash.int(@backingInt(name));
12534 const map = ges.shared.map.acquire();
12535 const map_mask = map.header().mask();
12536 const names_items = ges.shared.names.acquire().view().items(.@"0");
12537 var map_index = hash;
12538 while (true) : (map_index += 1) {
12539 map_index &= map_mask;
12540 const entry = &map.entries[map_index];
12541 const index = entry.acquire();
12542 if (index == .none) return null;
12543 if (entry.hash != hash) continue;
12544 if (names_items[@backingInt(index) - 1] == name) return index;
12545 }
12546 }
12547};
12548
12549pub fn getErrorValue(
12550 ip: *InternPool,
12551 gpa: Allocator,
12552 io: Io,
12553 tid: Zcu.PerThread.Id,
12554 name: NullTerminatedString,
12555) Allocator.Error!Zcu.ErrorInt {
12556 return @backingInt(try ip.global_error_set.getErrorValue(gpa, io, &ip.getLocal(tid).mutate.arena, name));
12557}
12558
12559pub fn getErrorValueIfExists(ip: *const InternPool, name: NullTerminatedString) ?Zcu.ErrorInt {
12560 return @backingInt(ip.global_error_set.getErrorValueIfExists(name) orelse return null);
12561}
12562
12563const PackedCallingConvention = packed struct(u18) {
12564 tag: std.lang.CallingConvention.Tag,
12565 /// May be ignored depending on `tag`.
12566 incoming_stack_alignment: Alignment,
12567 /// Interpretation depends on `tag`.
12568 extra: u4,
12569
12570 fn pack(cc: std.lang.CallingConvention) PackedCallingConvention {
12571 return switch (cc) {
12572 inline else => |pl, tag| switch (@TypeOf(pl)) {
12573 void => .{
12574 .tag = tag,
12575 .incoming_stack_alignment = .none, // unused
12576 .extra = 0, // unused
12577 },
12578 std.lang.CallingConvention.CommonOptions => .{
12579 .tag = tag,
12580 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12581 .extra = 0, // unused
12582 },
12583 std.lang.CallingConvention.X86RegparmOptions => .{
12584 .tag = tag,
12585 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12586 .extra = pl.register_params,
12587 },
12588 std.lang.CallingConvention.ArcInterruptOptions => .{
12589 .tag = tag,
12590 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12591 .extra = @backingInt(pl.type),
12592 },
12593 std.lang.CallingConvention.ArmInterruptOptions => .{
12594 .tag = tag,
12595 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12596 .extra = @backingInt(pl.type),
12597 },
12598 std.lang.CallingConvention.MicroblazeInterruptOptions => .{
12599 .tag = tag,
12600 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12601 .extra = @backingInt(pl.type),
12602 },
12603 std.lang.CallingConvention.MipsInterruptOptions => .{
12604 .tag = tag,
12605 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12606 .extra = @backingInt(pl.mode),
12607 },
12608 std.lang.CallingConvention.RiscvInterruptOptions => .{
12609 .tag = tag,
12610 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12611 .extra = @backingInt(pl.mode),
12612 },
12613 std.lang.CallingConvention.ShInterruptOptions => .{
12614 .tag = tag,
12615 .incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
12616 .extra = @backingInt(pl.save),
12617 },
12618 std.lang.CallingConvention.SpirvKernelOptions => .{
12619 .tag = tag,
12620 .incoming_stack_alignment = .none,
12621 .extra = 0,
12622 },
12623 std.lang.CallingConvention.SpirvFragmentOptions => .{
12624 .tag = tag,
12625 .incoming_stack_alignment = .none,
12626 .extra = @as(u4, @backingInt(pl.depth_assumption)) << 1 | @intFromBool(pl.pixel_centered_integer),
12627 },
12628 std.lang.CallingConvention.SpirvMeshOptions => .{
12629 .tag = tag,
12630 .incoming_stack_alignment = .none,
12631 .extra = @backingInt(pl.stage_output),
12632 },
12633 else => comptime unreachable,
12634 },
12635 };
12636 }
12637
12638 fn extraLen(cc: PackedCallingConvention) u3 {
12639 return switch (cc.tag) {
12640 .spirv_kernel, .spirv_task => 3,
12641 .spirv_mesh => 5,
12642 else => 0,
12643 };
12644 }
12645
12646 fn unpack(cc: PackedCallingConvention, trailing: []const u32) std.lang.CallingConvention {
12647 return switch (cc.tag) {
12648 inline else => |tag| @unionInit(
12649 std.lang.CallingConvention,
12650 @tagName(tag),
12651 switch (@FieldType(std.lang.CallingConvention, @tagName(tag))) {
12652 void => {},
12653 std.lang.CallingConvention.CommonOptions => .{
12654 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12655 },
12656 std.lang.CallingConvention.X86RegparmOptions => .{
12657 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12658 .register_params = @intCast(cc.extra),
12659 },
12660 std.lang.CallingConvention.ArcInterruptOptions => .{
12661 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12662 .type = @fromBackingInt(@intCast(cc.extra)),
12663 },
12664 std.lang.CallingConvention.ArmInterruptOptions => .{
12665 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12666 .type = @fromBackingInt(@intCast(cc.extra)),
12667 },
12668 std.lang.CallingConvention.MicroblazeInterruptOptions => .{
12669 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12670 .type = @fromBackingInt(@intCast(cc.extra)),
12671 },
12672 std.lang.CallingConvention.MipsInterruptOptions => .{
12673 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12674 .mode = @fromBackingInt(@intCast(cc.extra)),
12675 },
12676 std.lang.CallingConvention.RiscvInterruptOptions => .{
12677 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12678 .mode = @fromBackingInt(@intCast(cc.extra)),
12679 },
12680 std.lang.CallingConvention.ShInterruptOptions => .{
12681 .incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
12682 .save = @fromBackingInt(@intCast(cc.extra)),
12683 },
12684 std.lang.CallingConvention.SpirvKernelOptions => .{
12685 .x = trailing[0],
12686 .y = trailing[1],
12687 .z = trailing[2],
12688 },
12689 std.lang.CallingConvention.SpirvFragmentOptions => .{
12690 .pixel_centered_integer = @bitCast(@as(u1, @truncate(cc.extra))),
12691 .depth_assumption = @fromBackingInt(@intCast(@as(u2, @truncate(cc.extra >> 1)))),
12692 },
12693 std.lang.CallingConvention.SpirvMeshOptions => .{
12694 .stage_output = @fromBackingInt(@intCast(cc.extra)),
12695 .max_primitives = trailing[0],
12696 .max_vertices = trailing[1],
12697 .x = trailing[2],
12698 .y = trailing[3],
12699 .z = trailing[4],
12700 },
12701 else => comptime unreachable,
12702 },
12703 ),
12704 };
12705 }
12706};
12707
12708/// Asserts that `struct_type` is a non-packed struct type.
12709/// As well as calling this function, the caller must also populate these arrays:
12710/// * `field_types`
12711/// * `field_aligns`
12712/// * `field_runtime_order`
12713/// * `field_offsets`
12714pub fn resolveStructLayout(
12715 ip: *InternPool,
12716 io: Io,
12717 struct_type: Index,
12718 size: u32,
12719 alignment: Alignment,
12720 class: TypeClass,
12721) void {
12722 const unwrapped_index = struct_type.unwrap(ip);
12723
12724 const local = ip.getLocal(unwrapped_index.tid);
12725 local.mutate.extra.mutex.lockUncancelable(io);
12726 defer local.mutate.extra.mutex.unlock(io);
12727
12728 const extra_items = local.shared.extra.view().items(.@"0");
12729 const item = unwrapped_index.getItem(ip);
12730 assert(item.tag == .type_struct);
12731
12732 extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "size").?] = size;
12733 const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?]);
12734 flags.class = class;
12735 flags.alignment = alignment;
12736}
12737
12738/// Asserts that `union_type` is a non-packed union type.
12739/// As well as calling this function, the caller must also populate these arrays:
12740/// * `field_types`
12741/// * `field_aligns`
12742pub fn resolveUnionLayout(
12743 ip: *InternPool,
12744 io: Io,
12745 union_type: Index,
12746 enum_tag_type: Index,
12747 class: TypeClass,
12748 has_runtime_tag: bool,
12749 size: u32,
12750 padding: u32,
12751 alignment: Alignment,
12752) void {
12753 const unwrapped_index = union_type.unwrap(ip);
12754
12755 const local = ip.getLocal(unwrapped_index.tid);
12756 local.mutate.extra.mutex.lockUncancelable(io);
12757 defer local.mutate.extra.mutex.unlock(io);
12758
12759 const extra_items = local.shared.extra.view().items(.@"0");
12760 const item = unwrapped_index.getItem(ip);
12761 assert(item.tag == .type_union);
12762
12763 extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "enum_tag_type").?] = @backingInt(enum_tag_type);
12764 extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "size").?] = size;
12765 extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "padding").?] = padding;
12766 const flags: *Tag.TypeUnion.Flags = @ptrCast(&extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnion, "flags").?]);
12767 flags.class = class;
12768 flags.has_runtime_tag = has_runtime_tag;
12769 flags.alignment = alignment;
12770}
12771
12772/// Asserts that `struct_type` is a packed struct type.
12773pub fn resolvePackedStructLayout(
12774 ip: *InternPool,
12775 io: Io,
12776 struct_type: Index,
12777 backing_int_type: Index,
12778) void {
12779 const unwrapped_index = struct_type.unwrap(ip);
12780
12781 const local = ip.getLocal(unwrapped_index.tid);
12782 local.mutate.extra.mutex.lockUncancelable(io);
12783 defer local.mutate.extra.mutex.unlock(io);
12784
12785 const extra_items = local.shared.extra.view().items(.@"0");
12786 const item = unwrapped_index.getItem(ip);
12787 switch (item.tag) {
12788 .type_struct_packed_auto,
12789 .type_struct_packed_explicit,
12790 .type_struct_packed_auto_defaults,
12791 .type_struct_packed_explicit_defaults,
12792 => {},
12793 else => unreachable,
12794 }
12795
12796 extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "backing_int_type").?] = @backingInt(backing_int_type);
12797}
12798
12799/// Asserts that `union_type` is a packed union type.
12800pub fn resolvePackedUnionLayout(
12801 ip: *InternPool,
12802 io: Io,
12803 union_type: Index,
12804 enum_tag_type: Index,
12805 backing_int_type: Index,
12806) void {
12807 const unwrapped_index = union_type.unwrap(ip);
12808
12809 const local = ip.getLocal(unwrapped_index.tid);
12810 local.mutate.extra.mutex.lockUncancelable(io);
12811 defer local.mutate.extra.mutex.unlock(io);
12812
12813 const extra_items = local.shared.extra.view().items(.@"0");
12814 const item = unwrapped_index.getItem(ip);
12815 switch (item.tag) {
12816 .type_union_packed_auto,
12817 .type_union_packed_explicit,
12818 => {},
12819 else => unreachable,
12820 }
12821
12822 extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "enum_tag_type").?] = @backingInt(enum_tag_type);
12823 extra_items[item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "backing_int_type").?] = @backingInt(backing_int_type);
12824}
12825
12826/// Asserts that `enum_type` is an enum type.
12827pub fn resolveEnumLayout(
12828 ip: *InternPool,
12829 io: Io,
12830 enum_type: Index,
12831 int_tag_type: Index,
12832) void {
12833 const unwrapped_index = enum_type.unwrap(ip);
12834
12835 const local = ip.getLocal(unwrapped_index.tid);
12836 local.mutate.extra.mutex.lockUncancelable(io);
12837 defer local.mutate.extra.mutex.unlock(io);
12838
12839 const extra_items = local.shared.extra.view().items(.@"0");
12840 const item = unwrapped_index.getItem(ip);
12841 switch (item.tag) {
12842 .type_enum_auto,
12843 .type_enum_explicit,
12844 .type_enum_nonexhaustive,
12845 => {},
12846 else => unreachable,
12847 }
12848
12849 extra_items[item.data + std.meta.fieldIndex(Tag.TypeEnum, "int_tag_type").?] = @backingInt(int_tag_type);
12850}
12851
12852/// Sets the "want_layout" flag on the given struct, union, or enum type. Returns true if the flag
12853/// was *not* already set, meaning we have just discovered the first reference to this type's
12854/// layout. This flag is never reset to false, and exists purely as an optimization; for details,
12855/// see doc comments in `LoadedStructType`.
12856pub fn setWantTypeLayout(ip: *InternPool, io: Io, container_type: Index) bool {
12857 const unwrapped_index = container_type.unwrap(ip);
12858
12859 const local = ip.getLocal(unwrapped_index.tid);
12860 local.mutate.extra.mutex.lockUncancelable(io);
12861 defer local.mutate.extra.mutex.unlock(io);
12862
12863 const extra_items = local.shared.extra.view().items(.@"0");
12864 const item = unwrapped_index.getItem(ip);
12865 switch (item.tag) {
12866 .type_struct_packed_auto,
12867 .type_struct_packed_explicit,
12868 .type_struct_packed_auto_defaults,
12869 .type_struct_packed_explicit_defaults,
12870 => {
12871 const bits: *Tag.TypeStructPacked.Bits = @ptrCast(&extra_items[
12872 item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "bits").?
12873 ]);
12874 if (bits.want_layout) {
12875 return false;
12876 } else {
12877 bits.want_layout = true;
12878 return true;
12879 }
12880 },
12881
12882 .type_struct => {
12883 const flags: *Tag.TypeStruct.Flags = @ptrCast(&extra_items[
12884 item.data + std.meta.fieldIndex(Tag.TypeStruct, "flags").?
12885 ]);
12886 if (flags.want_layout) {
12887 return false;
12888 } else {
12889 flags.want_layout = true;
12890 return true;
12891 }
12892 },
12893
12894 .type_union_packed_auto,
12895 .type_union_packed_explicit,
12896 => {
12897 const bits: *Tag.TypeUnionPacked.Bits = @ptrCast(&extra_items[
12898 item.data + std.meta.fieldIndex(Tag.TypeUnionPacked, "bits").?
12899 ]);
12900 if (bits.want_layout) {
12901 return false;
12902 } else {
12903 bits.want_layout = true;
12904 return true;
12905 }
12906 },
12907
12908 .type_union => {
12909 const flags: *Tag.TypeUnion.Flags = @ptrCast(&extra_items[
12910 item.data + std.meta.fieldIndex(Tag.TypeUnion, "flags").?
12911 ]);
12912 if (flags.want_layout) {
12913 return false;
12914 } else {
12915 flags.want_layout = true;
12916 return true;
12917 }
12918 },
12919
12920 .type_enum_auto,
12921 .type_enum_explicit,
12922 .type_enum_nonexhaustive,
12923 => {
12924 const bits: *Tag.TypeEnum.Bits = @ptrCast(&extra_items[
12925 item.data + std.meta.fieldIndex(Tag.TypeEnum, "bits").?
12926 ]);
12927 if (bits.want_layout) {
12928 return false;
12929 } else {
12930 bits.want_layout = true;
12931 return true;
12932 }
12933 },
12934
12935 else => unreachable,
12936 }
12937}
12938
12939/// Like `setWantTypeLayout`, but for runtime analysis of a function body, using the
12940/// `FuncAnalysis.want_runtime_analysis` flag.
12941pub fn setWantRuntimeFnAnalysis(ip: *InternPool, io: Io, func_index: Index) bool {
12942 const unwrapped_index = func_index.unwrap(ip);
12943
12944 const local = ip.getLocal(unwrapped_index.tid);
12945 local.mutate.extra.mutex.lockUncancelable(io);
12946 defer local.mutate.extra.mutex.unlock(io);
12947
12948 const a = funcAnalysisPtr(ip, func_index);
12949 if (a.want_runtime_analysis) {
12950 return false;
12951 } else {
12952 a.want_runtime_analysis = true;
12953 return true;
12954 }
12955}
12956
12957/// Like `setWantTypeLayout`, but for runtime analysis of a `Nav`, using the `Nav.analysis.wanted` flag.
12958pub fn setWantNavAnalysis(ip: *InternPool, io: Io, nav_index: Nav.Index) bool {
12959 const unwrapped = nav_index.unwrap(ip);
12960
12961 const local = ip.getLocal(unwrapped.tid);
12962 local.mutate.extra.mutex.lockUncancelable(io);
12963 defer local.mutate.extra.mutex.unlock(io);
12964
12965 const navs = local.shared.navs.view();
12966
12967 if (navs.items(.analysis_namespace)[unwrapped.index] == .none) {
12968 return false;
12969 }
12970
12971 // Mutate `bits` atomically so that we don't introduce an illegal data race with `getNav`.
12972 const old_bits = @atomicRmw(
12973 Nav.Repr.Bits,
12974 &navs.items(.bits)[unwrapped.index],
12975 .Or,
12976 mask: {
12977 var mask: Nav.Repr.Bits = @bitCast(@as(u16, 0));
12978 mask.want_analysis = true;
12979 break :mask mask;
12980 },
12981 .monotonic,
12982 );
12983 return !old_bits.want_analysis;
12984}