zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 103cbaaa (189114B) - Raw


      1 //! This type provides a wrapper around a `*Zcu` for uses which require a thread `Id`.
      2 //! Any operation which mutates `InternPool` state lives here rather than on `Zcu`.
      3 
      4 const std = @import("std");
      5 const Allocator = std.mem.Allocator;
      6 const assert = std.debug.assert;
      7 const Ast = std.zig.Ast;
      8 const AstGen = std.zig.AstGen;
      9 const BigIntConst = std.math.big.int.Const;
     10 const BigIntMutable = std.math.big.int.Mutable;
     11 const Cache = std.Build.Cache;
     12 const log = std.log.scoped(.zcu);
     13 const mem = std.mem;
     14 const Zir = std.zig.Zir;
     15 const Zoir = std.zig.Zoir;
     16 const ZonGen = std.zig.ZonGen;
     17 const Io = std.Io;
     18 
     19 const Air = @import("../Air.zig");
     20 const Builtin = @import("../Builtin.zig");
     21 const build_options = @import("build_options");
     22 const builtin = @import("builtin");
     23 const dev = @import("../dev.zig");
     24 const InternPool = @import("../InternPool.zig");
     25 const AnalUnit = InternPool.AnalUnit;
     26 const introspect = @import("../introspect.zig");
     27 const Module = @import("../Package.zig").Module;
     28 const Sema = @import("../Sema.zig");
     29 const target_util = @import("../target.zig");
     30 const trace = @import("../tracy.zig").trace;
     31 const Type = @import("../Type.zig");
     32 const Value = @import("../Value.zig");
     33 const Zcu = @import("../Zcu.zig");
     34 const Compilation = @import("../Compilation.zig");
     35 const codegen = @import("../codegen.zig");
     36 const crash_report = @import("../crash_report.zig");
     37 
     38 zcu: *Zcu,
     39 
     40 /// Dense, per-thread unique index.
     41 tid: Id,
     42 
     43 pub const IdBacking = u7;
     44 pub const Id = if (InternPool.single_threaded) enum { main } else enum(IdBacking) { main, _ };
     45 
     46 pub fn activate(zcu: *Zcu, tid: Id) Zcu.PerThread {
     47     zcu.intern_pool.activate();
     48     return .{ .zcu = zcu, .tid = tid };
     49 }
     50 
     51 pub fn deactivate(pt: Zcu.PerThread) void {
     52     pt.zcu.intern_pool.deactivate();
     53 }
     54 
     55 fn deinitFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
     56     const zcu = pt.zcu;
     57     const gpa = zcu.gpa;
     58     const file = zcu.fileByIndex(file_index);
     59     log.debug("deinit File {f}", .{file.path.fmt(zcu.comp)});
     60     file.path.deinit(gpa);
     61     file.unload(gpa);
     62     if (file.prev_zir) |prev_zir| {
     63         prev_zir.deinit(gpa);
     64         gpa.destroy(prev_zir);
     65     }
     66     file.* = undefined;
     67 }
     68 
     69 pub fn destroyFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void {
     70     const gpa = pt.zcu.gpa;
     71     const file = pt.zcu.fileByIndex(file_index);
     72     pt.deinitFile(file_index);
     73     gpa.destroy(file);
     74 }
     75 
     76 /// Ensures that `file` has up-to-date ZIR. If not, loads the ZIR cache or runs
     77 /// AstGen as needed. Also updates `file.status`. Does not assume that `file.mod`
     78 /// is populated. Does not return `error.AnalysisFail` on AstGen failures.
     79 pub fn updateFile(
     80     pt: Zcu.PerThread,
     81     file_index: Zcu.File.Index,
     82     file: *Zcu.File,
     83 ) !void {
     84     dev.check(.ast_gen);
     85 
     86     const tracy = trace(@src());
     87     defer tracy.end();
     88 
     89     const zcu = pt.zcu;
     90     const comp = zcu.comp;
     91     const gpa = zcu.gpa;
     92     const io = comp.io;
     93 
     94     // In any case we need to examine the stat of the file to determine the course of action.
     95     var source_file = f: {
     96         const dir, const sub_path = file.path.openInfo(comp.dirs);
     97         break :f try dir.openFile(io, sub_path, .{});
     98     };
     99     defer source_file.close(io);
    100 
    101     const stat = try source_file.stat(io);
    102 
    103     const want_local_cache = switch (file.path.root) {
    104         .none, .local_cache => true,
    105         .global_cache, .zig_lib => false,
    106     };
    107 
    108     const hex_digest: Cache.HexDigest = d: {
    109         var h: Cache.HashHelper = .{};
    110         // As well as the file path, we also include the compiler version in case of backwards-incompatible ZIR changes.
    111         file.path.addToHasher(&h.hasher);
    112         h.addBytes(build_options.version);
    113         h.add(builtin.zig_backend);
    114         break :d h.final();
    115     };
    116 
    117     const cache_directory = if (want_local_cache) zcu.local_zir_cache else zcu.global_zir_cache;
    118     const zir_dir = cache_directory.handle;
    119 
    120     // Determine whether we need to reload the file from disk and redo parsing and AstGen.
    121     var lock: Io.File.Lock = switch (file.status) {
    122         .never_loaded, .retryable_failure => lock: {
    123             // First, load the cached ZIR code, if any.
    124             log.debug("AstGen checking cache: {f} (local={}, digest={s})", .{
    125                 file.path.fmt(comp), want_local_cache, &hex_digest,
    126             });
    127 
    128             break :lock .shared;
    129         },
    130         .astgen_failure, .success => lock: {
    131             const unchanged_metadata =
    132                 stat.size == file.stat.size and
    133                 stat.mtime.nanoseconds == file.stat.mtime.nanoseconds and
    134                 stat.inode == file.stat.inode;
    135 
    136             if (unchanged_metadata) {
    137                 log.debug("unmodified metadata of file: {f}", .{file.path.fmt(comp)});
    138                 return;
    139             }
    140 
    141             log.debug("metadata changed: {f}", .{file.path.fmt(comp)});
    142 
    143             break :lock .exclusive;
    144         },
    145     };
    146 
    147     // The old compile error, if any, is no longer relevant.
    148     pt.lockAndClearFileCompileError(file_index, file);
    149 
    150     // If `zir` is not null, and `prev_zir` is null, then `TrackedInst`s are associated with `zir`.
    151     // We need to keep it around!
    152     // As an optimization, also check `loweringFailed`; if true, but `prev_zir == null`, then this
    153     // file has never passed AstGen, so we actually need not cache the old ZIR.
    154     if (file.zir != null and file.prev_zir == null and !file.zir.?.loweringFailed()) {
    155         assert(file.prev_zir == null);
    156         const prev_zir_ptr = try gpa.create(Zir);
    157         file.prev_zir = prev_zir_ptr;
    158         prev_zir_ptr.* = file.zir.?;
    159         file.zir = null;
    160     }
    161 
    162     // If ZOIR is changing, then we need to invalidate dependencies on it
    163     if (file.zoir != null) file.zoir_invalidated = true;
    164 
    165     // We're going to re-load everything, so unload source, AST, ZIR, ZOIR.
    166     file.unload(gpa);
    167 
    168     // We ask for a lock in order to coordinate with other zig processes.
    169     // If another process is already working on this file, we will get the cached
    170     // version. Likewise if we're working on AstGen and another process asks for
    171     // the cached file, they'll get it.
    172     const cache_file = while (true) {
    173         break zir_dir.createFile(io, &hex_digest, .{
    174             .read = true,
    175             .truncate = false,
    176             .lock = lock,
    177         }) catch |err| switch (err) {
    178             error.NotDir => unreachable, // no dir components
    179             error.BadPathName => unreachable, // it's a hex encoded name
    180             error.NameTooLong => unreachable, // it's a fixed size name
    181             error.PipeBusy => unreachable, // it's not a pipe
    182             error.NoDevice => unreachable, // it's not a pipe
    183             error.WouldBlock => unreachable, // not asking for non-blocking I/O
    184             error.FileNotFound => {
    185                 // There are no dir components, so the only possibility should
    186                 // be that the directory behind the handle has been deleted,
    187                 // however we have observed on macOS two processes racing to do
    188                 // openat() with O_CREAT manifest in ENOENT.
    189                 //
    190                 // As a workaround, we retry with exclusive=true which
    191                 // disambiguates by returning EEXIST, indicating original
    192                 // failure was a race, or ENOENT, indicating deletion of the
    193                 // directory of our open handle.
    194                 if (!builtin.os.tag.isDarwin()) {
    195                     std.process.fatal("cache directory '{f}' unexpectedly removed during compiler execution", .{
    196                         cache_directory,
    197                     });
    198                 }
    199                 break zir_dir.createFile(io, &hex_digest, .{
    200                     .read = true,
    201                     .truncate = false,
    202                     .lock = lock,
    203                     .exclusive = true,
    204                 }) catch |excl_err| switch (excl_err) {
    205                     error.PathAlreadyExists => continue,
    206                     error.FileNotFound => {
    207                         std.process.fatal("cache directory '{f}' unexpectedly removed during compiler execution", .{
    208                             cache_directory,
    209                         });
    210                     },
    211                     else => |e| return e,
    212                 };
    213             },
    214 
    215             else => |e| return e, // Retryable errors are handled at callsite.
    216         };
    217     };
    218     defer cache_file.close(io);
    219 
    220     // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers.
    221     const ignore_hit = comp.time_report != null;
    222 
    223     const need_update = while (true) {
    224         const result = switch (file.getMode()) {
    225             inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode),
    226         };
    227         switch (result) {
    228             .success => if (!ignore_hit) {
    229                 log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)});
    230                 break false;
    231             },
    232             .invalid => {},
    233             .truncated => log.warn("unexpected EOF reading cached ZIR for {f}", .{file.path.fmt(comp)}),
    234             .stale => log.debug("AstGen cache stale: {f}", .{file.path.fmt(comp)}),
    235         }
    236 
    237         // If we already have the exclusive lock then it is our job to update.
    238         if (builtin.os.tag == .wasi or lock == .exclusive) break true;
    239         // Otherwise, unlock to give someone a chance to get the exclusive lock
    240         // and then upgrade to an exclusive lock.
    241         cache_file.unlock(io);
    242         lock = .exclusive;
    243         try cache_file.lock(io, lock);
    244     };
    245 
    246     if (need_update) {
    247         var cache_file_writer: Io.File.Writer = .init(cache_file, io, &.{});
    248 
    249         if (stat.size > std.math.maxInt(u32))
    250             return error.FileTooBig;
    251 
    252         const source = try gpa.allocSentinel(u8, @intCast(stat.size), 0);
    253         defer if (file.source == null) gpa.free(source);
    254         var source_fr = source_file.reader(io, &.{});
    255         source_fr.size = stat.size;
    256         source_fr.interface.readSliceAll(source) catch |err| switch (err) {
    257             error.ReadFailed => return source_fr.err.?,
    258             error.EndOfStream => return error.UnexpectedEndOfFile,
    259         };
    260 
    261         file.source = source;
    262 
    263         var timer = comp.startTimer();
    264         // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen.
    265         file.tree = try Ast.parse(gpa, source, file.getMode());
    266         if (timer.finish()) |ns_parse| {
    267             comp.mutex.lockUncancelable(io);
    268             defer comp.mutex.unlock(io);
    269             comp.time_report.?.stats.cpu_ns_parse += ns_parse;
    270         }
    271 
    272         timer = comp.startTimer();
    273         switch (file.getMode()) {
    274             .zig => {
    275                 file.zir = try AstGen.generate(gpa, file.tree.?);
    276                 Zcu.saveZirCache(gpa, &cache_file_writer, stat, file.zir.?) catch |err| switch (err) {
    277                     error.OutOfMemory => |e| return e,
    278                     else => log.warn("unable to write cached ZIR code for {f} to {f}{s}: {t}", .{
    279                         file.path.fmt(comp), cache_directory, &hex_digest, err,
    280                     }),
    281                 };
    282             },
    283             .zon => {
    284                 file.zoir = try ZonGen.generate(gpa, file.tree.?, .{});
    285                 Zcu.saveZoirCache(&cache_file_writer, stat, file.zoir.?) catch |err| {
    286                     log.warn("unable to write cached ZOIR code for {f} to {f}{s}: {t}", .{
    287                         file.path.fmt(comp), cache_directory, &hex_digest, err,
    288                     });
    289                 };
    290             },
    291         }
    292 
    293         cache_file_writer.end() catch |err| switch (err) {
    294             error.WriteFailed => return cache_file_writer.err.?,
    295             else => |e| return e,
    296         };
    297 
    298         if (timer.finish()) |ns_astgen| {
    299             comp.mutex.lockUncancelable(io);
    300             defer comp.mutex.unlock(io);
    301             comp.time_report.?.stats.cpu_ns_astgen += ns_astgen;
    302         }
    303 
    304         log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)});
    305     }
    306 
    307     file.stat = .{
    308         .size = stat.size,
    309         .inode = stat.inode,
    310         .mtime = stat.mtime,
    311     };
    312 
    313     // Now, `zir` or `zoir` is definitely populated and up-to-date.
    314     // Mark file successes/failures as needed.
    315 
    316     switch (file.getMode()) {
    317         .zig => {
    318             if (file.zir.?.hasCompileErrors()) {
    319                 comp.mutex.lockUncancelable(io);
    320                 defer comp.mutex.unlock(io);
    321                 try zcu.failed_files.putNoClobber(gpa, file_index, null);
    322             }
    323             if (file.zir.?.loweringFailed()) {
    324                 file.status = .astgen_failure;
    325             } else {
    326                 file.status = .success;
    327             }
    328         },
    329         .zon => {
    330             if (file.zoir.?.hasCompileErrors()) {
    331                 file.status = .astgen_failure;
    332                 comp.mutex.lockUncancelable(io);
    333                 defer comp.mutex.unlock(io);
    334                 try zcu.failed_files.putNoClobber(gpa, file_index, null);
    335             } else {
    336                 file.status = .success;
    337             }
    338         },
    339     }
    340 
    341     switch (file.status) {
    342         .never_loaded => unreachable,
    343         .retryable_failure => unreachable,
    344         .astgen_failure, .success => {},
    345     }
    346 }
    347 
    348 fn loadZirZoirCache(
    349     zcu: *Zcu,
    350     cache_file: Io.File,
    351     stat: Io.File.Stat,
    352     file: *Zcu.File,
    353     comptime mode: Ast.Mode,
    354 ) !enum { success, invalid, truncated, stale } {
    355     assert(file.getMode() == mode);
    356 
    357     const gpa = zcu.gpa;
    358     const io = zcu.comp.io;
    359 
    360     const Header = switch (mode) {
    361         .zig => Zir.Header,
    362         .zon => Zoir.Header,
    363     };
    364 
    365     var buffer: [2000]u8 = undefined;
    366     var cache_fr = cache_file.reader(io, &buffer);
    367     cache_fr.size = stat.size;
    368     const cache_br = &cache_fr.interface;
    369 
    370     // First we read the header to determine the lengths of arrays.
    371     const header = (cache_br.takeStructPointer(Header) catch |err| switch (err) {
    372         error.ReadFailed => return cache_fr.err.?,
    373         // This can happen if Zig bails out of this function between creating
    374         // the cached file and writing it.
    375         error.EndOfStream => return .invalid,
    376         else => |e| return e,
    377     }).*;
    378 
    379     const unchanged_metadata =
    380         stat.size == header.stat_size and
    381         stat.mtime.nanoseconds == header.stat_mtime and
    382         stat.inode == header.stat_inode;
    383 
    384     if (!unchanged_metadata) {
    385         return .stale;
    386     }
    387 
    388     switch (mode) {
    389         .zig => file.zir = Zcu.loadZirCacheBody(gpa, header, cache_br) catch |err| switch (err) {
    390             error.ReadFailed => return cache_fr.err.?,
    391             error.EndOfStream => return .truncated,
    392             else => |e| return e,
    393         },
    394         .zon => file.zoir = Zcu.loadZoirCacheBody(gpa, header, cache_br) catch |err| switch (err) {
    395             error.ReadFailed => return cache_fr.err.?,
    396             error.EndOfStream => return .truncated,
    397             else => |e| return e,
    398         },
    399     }
    400 
    401     return .success;
    402 }
    403 
    404 const UpdatedFile = struct {
    405     file: *Zcu.File,
    406     inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
    407 };
    408 
    409 fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile)) void {
    410     for (updated_files.values()) |*elem| elem.inst_map.deinit(gpa);
    411     updated_files.deinit(gpa);
    412 }
    413 
    414 pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
    415     assert(pt.tid == .main);
    416     const zcu = pt.zcu;
    417     const comp = zcu.comp;
    418     const ip = &zcu.intern_pool;
    419     const gpa = comp.gpa;
    420     const io = comp.io;
    421 
    422     // We need to visit every updated File for every TrackedInst in InternPool.
    423     // This only includes Zig files; ZON files are omitted.
    424     var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty;
    425     defer cleanupUpdatedFiles(gpa, &updated_files);
    426 
    427     for (zcu.import_table.keys()) |file_index| {
    428         if (!zcu.alive_files.contains(file_index)) continue;
    429         const file = zcu.fileByIndex(file_index);
    430         assert(file.status == .success);
    431         if (file.module_changed) {
    432             try updated_files.putNoClobber(gpa, file_index, .{
    433                 .file = file,
    434                 // We intentionally don't map any instructions here; that's the point, the whole file is outdated!
    435                 .inst_map = .{},
    436             });
    437             continue;
    438         }
    439         switch (file.getMode()) {
    440             .zig => {}, // logic below
    441             .zon => {
    442                 if (file.zoir_invalidated) {
    443                     try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index });
    444                     file.zoir_invalidated = false;
    445                 }
    446                 continue;
    447             },
    448         }
    449         const old_zir = file.prev_zir orelse continue;
    450         const new_zir = file.zir.?;
    451         const gop = try updated_files.getOrPut(gpa, file_index);
    452         assert(!gop.found_existing);
    453         gop.value_ptr.* = .{
    454             .file = file,
    455             .inst_map = .{},
    456         };
    457         try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map);
    458     }
    459 
    460     if (updated_files.count() == 0)
    461         return;
    462 
    463     for (ip.locals, 0..) |*local, tid| {
    464         const tracked_insts_list = local.getMutableTrackedInsts(gpa, io);
    465         for (tracked_insts_list.viewAllowEmpty().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| {
    466             const file_index = tracked_inst.file;
    467             const updated_file = updated_files.get(file_index) orelse continue;
    468 
    469             const file = updated_file.file;
    470 
    471             const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts
    472             const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{
    473                 .tid = @enumFromInt(tid),
    474                 .index = @intCast(tracked_inst_unwrapped_index),
    475             }).wrap(ip);
    476             const new_inst = updated_file.inst_map.get(old_inst) orelse {
    477                 // Tracking failed for this instruction due to changes in the ZIR.
    478                 // Invalidate associated `src_hash` deps.
    479                 log.debug("tracking failed for %{d}", .{old_inst});
    480                 tracked_inst.inst = .lost;
    481                 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
    482                 continue;
    483             };
    484             tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst);
    485 
    486             const old_zir = file.prev_zir.?.*;
    487             const new_zir = file.zir.?;
    488             const old_tag = old_zir.instructions.items(.tag)[@intFromEnum(old_inst)];
    489             const old_data = old_zir.instructions.items(.data)[@intFromEnum(old_inst)];
    490 
    491             switch (old_tag) {
    492                 .declaration => {
    493                     const old_line = old_zir.getDeclaration(old_inst).src_line;
    494                     const new_line = new_zir.getDeclaration(new_inst).src_line;
    495                     if (old_line != new_line) {
    496                         try comp.queueJob(.{ .update_line_number = tracked_inst_index });
    497                     }
    498                 },
    499                 else => {},
    500             }
    501 
    502             if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
    503                 if (new_zir.getAssociatedSrcHash(new_inst)) |new_hash| {
    504                     if (std.zig.srcHashEql(old_hash, new_hash)) {
    505                         break :hash_changed;
    506                     }
    507                     log.debug("hash for (%{d} -> %{d}) changed: {x} -> {x}", .{
    508                         old_inst, new_inst, &old_hash, &new_hash,
    509                     });
    510                 }
    511                 // The source hash associated with this instruction changed - invalidate relevant dependencies.
    512                 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index });
    513             }
    514 
    515             // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
    516             const has_namespace = switch (old_tag) {
    517                 .extended => switch (old_data.extended.opcode) {
    518                     .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
    519                     else => false,
    520                 },
    521                 else => false,
    522             };
    523             if (!has_namespace) continue;
    524 
    525             // Value is whether the declaration is `pub`.
    526             var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, bool) = .empty;
    527             defer old_names.deinit(zcu.gpa);
    528             {
    529                 var it = old_zir.declIterator(old_inst);
    530                 while (it.next()) |decl_inst| {
    531                     const old_decl = old_zir.getDeclaration(decl_inst);
    532                     if (old_decl.name == .empty) continue;
    533                     const name_ip = try zcu.intern_pool.getOrPutString(
    534                         zcu.gpa,
    535                         io,
    536                         pt.tid,
    537                         old_zir.nullTerminatedString(old_decl.name),
    538                         .no_embedded_nulls,
    539                     );
    540                     try old_names.put(zcu.gpa, name_ip, old_decl.is_pub);
    541                 }
    542             }
    543             var any_change = false;
    544             {
    545                 var it = new_zir.declIterator(new_inst);
    546                 while (it.next()) |decl_inst| {
    547                     const new_decl = new_zir.getDeclaration(decl_inst);
    548                     if (new_decl.name == .empty) continue;
    549                     const name_ip = try zcu.intern_pool.getOrPutString(
    550                         zcu.gpa,
    551                         io,
    552                         pt.tid,
    553                         new_zir.nullTerminatedString(new_decl.name),
    554                         .no_embedded_nulls,
    555                     );
    556                     if (old_names.fetchSwapRemove(name_ip)) |kv| {
    557                         if (kv.value == new_decl.is_pub) continue;
    558                     }
    559                     // Name added, or changed whether it's pub
    560                     any_change = true;
    561                     try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
    562                         .namespace = tracked_inst_index,
    563                         .name = name_ip,
    564                     } });
    565                 }
    566             }
    567             // The only elements remaining in `old_names` now are any names which were removed.
    568             for (old_names.keys()) |name_ip| {
    569                 any_change = true;
    570                 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{
    571                     .namespace = tracked_inst_index,
    572                     .name = name_ip,
    573                 } });
    574             }
    575 
    576             if (any_change) {
    577                 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace = tracked_inst_index });
    578             }
    579         }
    580     }
    581 
    582     try ip.rehashTrackedInsts(gpa, io, pt.tid);
    583 
    584     for (updated_files.keys(), updated_files.values()) |file_index, updated_file| {
    585         const file = updated_file.file;
    586 
    587         if (file.prev_zir) |prev_zir| {
    588             prev_zir.deinit(gpa);
    589             gpa.destroy(prev_zir);
    590             file.prev_zir = null;
    591         }
    592         file.module_changed = false;
    593 
    594         // For every file which has changed, re-scan the namespace of the file's root struct type.
    595         // These types are special-cased because they don't have an enclosing declaration which will
    596         // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
    597         // now because this work is fast (no actual Sema work is happening, we're just updating the
    598         // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
    599         // will track some instructions.
    600         try pt.updateFileNamespace(file_index);
    601     }
    602 }
    603 
    604 /// Ensures that `zcu.fileRootType` on this `file_index` gives an up-to-date answer.
    605 /// Returns `error.AnalysisFail` if the file has an error.
    606 pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
    607     const file_root_type = pt.zcu.fileRootType(file_index);
    608     if (file_root_type != .none) {
    609         if (pt.ensureTypeUpToDate(file_root_type)) |_| {
    610             return;
    611         } else |err| switch (err) {
    612             error.AnalysisFail => {
    613                 // The file's root `struct_decl` has, at some point, been lost, because the file failed AstGen.
    614                 // Clear `file_root_type`, and try the `semaFile` call below, in case the instruction has since
    615                 // been discovered under a new `TrackedInst.Index`.
    616                 pt.zcu.setFileRootType(file_index, .none);
    617             },
    618             else => |e| return e,
    619         }
    620     }
    621     return pt.semaFile(file_index);
    622 }
    623 
    624 /// Ensures that all memoized state on `Zcu` is up-to-date, performing re-analysis if necessary.
    625 /// Returns `error.AnalysisFail` if an analysis error is encountered; the caller is free to ignore
    626 /// this, since the error is already registered, but it must not use the value of memoized fields.
    627 pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.SemaError!void {
    628     const tracy = trace(@src());
    629     defer tracy.end();
    630 
    631     const zcu = pt.zcu;
    632     const gpa = zcu.gpa;
    633 
    634     const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
    635 
    636     log.debug("ensureMemoizedStateUpToDate", .{});
    637 
    638     assert(!zcu.analysis_in_progress.contains(unit));
    639 
    640     const was_outdated = zcu.outdated.swapRemove(unit) or zcu.potentially_outdated.swapRemove(unit);
    641     const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit);
    642 
    643     if (was_outdated) {
    644         dev.check(.incremental);
    645         _ = zcu.outdated_ready.swapRemove(unit);
    646         // No need for `deleteUnitExports` because we never export anything.
    647         zcu.deleteUnitReferences(unit);
    648         zcu.deleteUnitCompileLogs(unit);
    649         if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| {
    650             kv.value.destroy(gpa);
    651         }
    652         _ = zcu.transitive_failed_analysis.swapRemove(unit);
    653     } else {
    654         if (prev_failed) return error.AnalysisFail;
    655         // We use an arbitrary element to check if the state has been resolved yet.
    656         const to_check: Zcu.BuiltinDecl = switch (stage) {
    657             .main => .Type,
    658             .panic => .panic,
    659             .va_list => .VaList,
    660             .assembly => .assembly,
    661         };
    662         if (zcu.builtin_decl_values.get(to_check) != .none) return;
    663     }
    664 
    665     if (zcu.comp.debugIncremental()) {
    666         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit);
    667         info.last_update_gen = zcu.generation;
    668         info.deps.clearRetainingCapacity();
    669     }
    670 
    671     const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed|
    672         .{ any_changed or prev_failed, false }
    673     else |err| switch (err) {
    674         error.AnalysisFail => res: {
    675             if (!zcu.failed_analysis.contains(unit)) {
    676                 // If this unit caused the error, it would have an entry in `failed_analysis`.
    677                 // Since it does not, this must be a transitive failure.
    678                 try zcu.transitive_failed_analysis.put(gpa, unit, {});
    679                 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(unit)});
    680             }
    681             break :res .{ !prev_failed, true };
    682         },
    683         error.OutOfMemory => {
    684             // TODO: same as for `ensureComptimeUnitUpToDate` etc
    685             return error.OutOfMemory;
    686         },
    687         error.Canceled => |e| return e,
    688         error.ComptimeReturn => unreachable,
    689         error.ComptimeBreak => unreachable,
    690     };
    691 
    692     if (was_outdated) {
    693         const dependee: InternPool.Dependee = .{ .memoized_state = stage };
    694         if (any_changed) {
    695             try zcu.markDependeeOutdated(.marked_po, dependee);
    696         } else {
    697             try zcu.markPoDependeeUpToDate(dependee);
    698         }
    699     }
    700 
    701     if (new_failed) return error.AnalysisFail;
    702 }
    703 
    704 fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.CompileError!bool {
    705     const zcu = pt.zcu;
    706     const ip = &zcu.intern_pool;
    707     const comp = zcu.comp;
    708     const gpa = comp.gpa;
    709     const io = comp.io;
    710 
    711     const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
    712 
    713     try zcu.analysis_in_progress.putNoClobber(gpa, unit, {});
    714     defer assert(zcu.analysis_in_progress.swapRemove(unit));
    715 
    716     // Before we begin, collect:
    717     // * The type `std`, and its namespace
    718     // * The type `std.builtin`, and its namespace
    719     // * A semi-reasonable source location
    720     const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?;
    721     try pt.ensureFileAnalyzed(std_file_index);
    722     const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index));
    723     const std_namespace = std_type.getNamespaceIndex(zcu);
    724     try pt.ensureNamespaceUpToDate(std_namespace);
    725     const builtin_str = try ip.getOrPutString(gpa, io, pt.tid, "builtin", .no_embedded_nulls);
    726     const builtin_nav = zcu.namespacePtr(std_namespace).pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse
    727         @panic("lib/std.zig is corrupt and missing 'builtin'");
    728     try pt.ensureNavValUpToDate(builtin_nav);
    729     const builtin_type: Type = .fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val);
    730     const builtin_namespace = builtin_type.getNamespaceIndex(zcu);
    731     try pt.ensureNamespaceUpToDate(builtin_namespace);
    732     const src: Zcu.LazySrcLoc = .{
    733         .base_node_inst = builtin_type.typeDeclInst(zcu).?,
    734         .offset = .{ .byte_abs = 0 },
    735     };
    736 
    737     var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
    738     defer analysis_arena.deinit();
    739 
    740     var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
    741     defer comptime_err_ret_trace.deinit();
    742 
    743     var sema: Sema = .{
    744         .pt = pt,
    745         .gpa = gpa,
    746         .arena = analysis_arena.allocator(),
    747         .code = .{ .instructions = .empty, .string_bytes = &.{}, .extra = &.{} },
    748         .owner = unit,
    749         .func_index = .none,
    750         .func_is_naked = false,
    751         .fn_ret_ty = .void,
    752         .fn_ret_ty_ies = null,
    753         .comptime_err_ret_trace = &comptime_err_ret_trace,
    754     };
    755     defer sema.deinit();
    756 
    757     var block: Sema.Block = .{
    758         .parent = null,
    759         .sema = &sema,
    760         .namespace = std_namespace,
    761         .instructions = .{},
    762         .inlining = null,
    763         .comptime_reason = .{ .reason = .{
    764             .src = src,
    765             .r = .{ .simple = .type },
    766         } },
    767         .src_base_inst = src.base_node_inst,
    768         .type_name_ctx = .empty,
    769     };
    770     defer block.instructions.deinit(gpa);
    771 
    772     return sema.analyzeMemoizedState(&block, src, builtin_namespace, stage);
    773 }
    774 
    775 /// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis
    776 /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
    777 /// free to ignore this, since the error is already registered.
    778 pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.SemaError!void {
    779     const tracy = trace(@src());
    780     defer tracy.end();
    781 
    782     const zcu = pt.zcu;
    783     const gpa = zcu.gpa;
    784 
    785     const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
    786 
    787     log.debug("ensureComptimeUnitUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
    788 
    789     assert(!zcu.analysis_in_progress.contains(anal_unit));
    790 
    791     // Determine whether or not this `ComptimeUnit` is outdated. For this kind of `AnalUnit`, that's
    792     // the only indicator as to whether or not analysis is required; when a `ComptimeUnit` is first
    793     // created, it's marked as outdated.
    794     //
    795     // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
    796     // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
    797     // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
    798     // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
    799 
    800     const was_outdated = zcu.outdated.swapRemove(anal_unit) or
    801         zcu.potentially_outdated.swapRemove(anal_unit);
    802 
    803     if (was_outdated) {
    804         _ = zcu.outdated_ready.swapRemove(anal_unit);
    805         // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
    806         if (dev.env.supports(.incremental)) {
    807             zcu.deleteUnitExports(anal_unit);
    808             zcu.deleteUnitReferences(anal_unit);
    809             zcu.deleteUnitCompileLogs(anal_unit);
    810             if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
    811                 kv.value.destroy(gpa);
    812             }
    813             _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
    814             zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
    815         }
    816     } else {
    817         // We can trust the current information about this unit.
    818         if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail;
    819         if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail;
    820         return;
    821     }
    822 
    823     if (zcu.comp.debugIncremental()) {
    824         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
    825         info.last_update_gen = zcu.generation;
    826         info.deps.clearRetainingCapacity();
    827     }
    828 
    829     const unit_tracking = zcu.trackUnitSema(
    830         "comptime",
    831         zcu.intern_pool.getComptimeUnit(cu_id).zir_index,
    832     );
    833     defer unit_tracking.end(zcu);
    834 
    835     return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) {
    836         error.AnalysisFail => {
    837             if (!zcu.failed_analysis.contains(anal_unit)) {
    838                 // If this unit caused the error, it would have an entry in `failed_analysis`.
    839                 // Since it does not, this must be a transitive failure.
    840                 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
    841                 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
    842             }
    843             return error.AnalysisFail;
    844         },
    845         error.OutOfMemory => {
    846             // TODO: it's unclear how to gracefully handle this.
    847             // To report the error cleanly, we need to add a message to `failed_analysis` and a
    848             // corresponding entry to `retryable_failures`; but either of these things is quite
    849             // likely to OOM at this point.
    850             // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
    851             // for reporting OOM errors without allocating.
    852             return error.OutOfMemory;
    853         },
    854         error.Canceled => |e| return e,
    855         error.ComptimeReturn => unreachable,
    856         error.ComptimeBreak => unreachable,
    857     };
    858 }
    859 
    860 /// Re-analyzes a `ComptimeUnit`. The unit has already been determined to be out-of-date, and old
    861 /// side effects (exports/references/etc) have been dropped. If semantic analysis fails, this
    862 /// function will return `error.AnalysisFail`, and it is the caller's reponsibility to add an entry
    863 /// to `transitive_failed_analysis` if necessary.
    864 fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.CompileError!void {
    865     const zcu = pt.zcu;
    866     const ip = &zcu.intern_pool;
    867     const comp = zcu.comp;
    868     const gpa = comp.gpa;
    869     const io = comp.io;
    870 
    871     const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
    872     const comptime_unit = ip.getComptimeUnit(cu_id);
    873 
    874     log.debug("analyzeComptimeUnit {f}", .{zcu.fmtAnalUnit(anal_unit)});
    875 
    876     const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
    877     const file = zcu.fileByIndex(inst_resolved.file);
    878     const zir = file.zir.?;
    879 
    880     try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
    881     defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
    882 
    883     var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
    884     defer analysis_arena.deinit();
    885 
    886     var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
    887     defer comptime_err_ret_trace.deinit();
    888 
    889     var sema: Sema = .{
    890         .pt = pt,
    891         .gpa = gpa,
    892         .arena = analysis_arena.allocator(),
    893         .code = zir,
    894         .owner = anal_unit,
    895         .func_index = .none,
    896         .func_is_naked = false,
    897         .fn_ret_ty = .void,
    898         .fn_ret_ty_ies = null,
    899         .comptime_err_ret_trace = &comptime_err_ret_trace,
    900     };
    901     defer sema.deinit();
    902 
    903     // The comptime unit declares on the source of the corresponding `comptime` declaration.
    904     try sema.declareDependency(.{ .src_hash = comptime_unit.zir_index });
    905 
    906     var block: Sema.Block = .{
    907         .parent = null,
    908         .sema = &sema,
    909         .namespace = comptime_unit.namespace,
    910         .instructions = .{},
    911         .inlining = null,
    912         .comptime_reason = .{ .reason = .{
    913             .src = .{
    914                 .base_node_inst = comptime_unit.zir_index,
    915                 .offset = .{ .token_offset = .zero },
    916             },
    917             .r = .{ .simple = .comptime_keyword },
    918         } },
    919         .src_base_inst = comptime_unit.zir_index,
    920         .type_name_ctx = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.comptime", .{
    921             Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip).fmt(ip),
    922         }, .no_embedded_nulls),
    923     };
    924     defer block.instructions.deinit(gpa);
    925 
    926     const zir_decl = zir.getDeclaration(inst_resolved.inst);
    927     assert(zir_decl.kind == .@"comptime");
    928     assert(zir_decl.type_body == null);
    929     assert(zir_decl.align_body == null);
    930     assert(zir_decl.linksection_body == null);
    931     assert(zir_decl.addrspace_body == null);
    932     const value_body = zir_decl.value_body.?;
    933 
    934     const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
    935     assert(result_ref == .void_value); // AstGen should always uphold this
    936 
    937     // Nothing else to do -- for a comptime decl, all we care about are the side effects.
    938     // Just make sure to `flushExports`.
    939     try sema.flushExports();
    940 }
    941 
    942 /// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis
    943 /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
    944 /// free to ignore this, since the error is already registered.
    945 pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
    946     const tracy = trace(@src());
    947     defer tracy.end();
    948 
    949     // TODO: document this elsewhere mlugg!
    950     // For my own benefit, here's how a namespace update for a normal (non-file-root) type works:
    951     // `const S = struct { ... };`
    952     // We are adding or removing a declaration within this `struct`.
    953     // * `S` registers a dependency on `.{ .src_hash = (declaration of S) }`
    954     // * Any change to the `struct` body -- including changing a declaration -- invalidates this
    955     // * `S` is re-analyzed, but notes:
    956     //   * there is an existing struct instance (at this `TrackedInst` with these captures)
    957     //   * the struct's resolution is up-to-date (because nothing about the fields changed)
    958     // * so, it uses the same `struct`
    959     // * but this doesn't stop it from updating the namespace!
    960     //   * we basically do `scanDecls`, updating the namespace as needed
    961     // * so everyone lived happily ever after
    962 
    963     const zcu = pt.zcu;
    964     const gpa = zcu.gpa;
    965     const ip = &zcu.intern_pool;
    966 
    967     _ = zcu.nav_val_analysis_queued.swapRemove(nav_id);
    968 
    969     const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
    970     const nav = ip.getNav(nav_id);
    971 
    972     log.debug("ensureNavValUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
    973 
    974     assert(!zcu.analysis_in_progress.contains(anal_unit));
    975 
    976     // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the
    977     // status is `.unresolved`, which indicates that the value is outdated because it has *never*
    978     // been analyzed so far.
    979     //
    980     // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
    981     // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
    982     // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
    983     // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
    984 
    985     const was_outdated = zcu.outdated.swapRemove(anal_unit) or
    986         zcu.potentially_outdated.swapRemove(anal_unit);
    987 
    988     const prev_failed = zcu.failed_analysis.contains(anal_unit) or
    989         zcu.transitive_failed_analysis.contains(anal_unit);
    990 
    991     if (was_outdated) {
    992         dev.check(.incremental);
    993         _ = zcu.outdated_ready.swapRemove(anal_unit);
    994         zcu.deleteUnitExports(anal_unit);
    995         zcu.deleteUnitReferences(anal_unit);
    996         zcu.deleteUnitCompileLogs(anal_unit);
    997         if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
    998             kv.value.destroy(gpa);
    999         }
   1000         _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
   1001         ip.removeDependenciesForDepender(gpa, anal_unit);
   1002     } else {
   1003         // We can trust the current information about this unit.
   1004         if (prev_failed) return error.AnalysisFail;
   1005         switch (nav.status) {
   1006             .unresolved, .type_resolved => {},
   1007             .fully_resolved => return,
   1008         }
   1009     }
   1010 
   1011     if (zcu.comp.debugIncremental()) {
   1012         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
   1013         info.last_update_gen = zcu.generation;
   1014         info.deps.clearRetainingCapacity();
   1015     }
   1016 
   1017     const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip));
   1018     defer unit_tracking.end(zcu);
   1019 
   1020     const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
   1021         break :res .{
   1022             // If the unit has gone from failed to success, we still need to invalidate the dependencies.
   1023             result.val_changed or prev_failed,
   1024             false,
   1025         };
   1026     } else |err| switch (err) {
   1027         error.AnalysisFail => res: {
   1028             if (!zcu.failed_analysis.contains(anal_unit)) {
   1029                 // If this unit caused the error, it would have an entry in `failed_analysis`.
   1030                 // Since it does not, this must be a transitive failure.
   1031                 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
   1032                 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1033             }
   1034             break :res .{ !prev_failed, true };
   1035         },
   1036         error.OutOfMemory => {
   1037             // TODO: it's unclear how to gracefully handle this.
   1038             // To report the error cleanly, we need to add a message to `failed_analysis` and a
   1039             // corresponding entry to `retryable_failures`; but either of these things is quite
   1040             // likely to OOM at this point.
   1041             // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
   1042             // for reporting OOM errors without allocating.
   1043             return error.OutOfMemory;
   1044         },
   1045         error.Canceled => |e| return e,
   1046         error.ComptimeReturn => unreachable,
   1047         error.ComptimeBreak => unreachable,
   1048     };
   1049 
   1050     if (was_outdated) {
   1051         const dependee: InternPool.Dependee = .{ .nav_val = nav_id };
   1052         if (invalidate_value) {
   1053             // This dependency was marked as PO, meaning dependees were waiting
   1054             // on its analysis result, and it has turned out to be outdated.
   1055             // Update dependees accordingly.
   1056             try zcu.markDependeeOutdated(.marked_po, dependee);
   1057         } else {
   1058             // This dependency was previously PO, but turned out to be up-to-date.
   1059             // We do not need to queue successive analysis.
   1060             try zcu.markPoDependeeUpToDate(dependee);
   1061         }
   1062     }
   1063 
   1064     // If there isn't a type annotation, then we have also just resolved the type. That means the
   1065     // the type is up-to-date, so it won't have the chance to mark its own dependency on the value;
   1066     // we must do that ourselves.
   1067     type_deps_on_val: {
   1068         const inst_resolved = nav.analysis.?.zir_index.resolveFull(ip) orelse break :type_deps_on_val;
   1069         const file = zcu.fileByIndex(inst_resolved.file);
   1070         const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst);
   1071         if (zir_decl.type_body != null) break :type_deps_on_val;
   1072         // The type does indeed depend on the value. We are responsible for populating all state of
   1073         // the `nav_ty`, including exports, references, errors, and dependencies.
   1074         const ty_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
   1075         const ty_was_outdated = zcu.outdated.swapRemove(ty_unit) or
   1076             zcu.potentially_outdated.swapRemove(ty_unit);
   1077         if (ty_was_outdated) {
   1078             _ = zcu.outdated_ready.swapRemove(ty_unit);
   1079             zcu.deleteUnitExports(ty_unit);
   1080             zcu.deleteUnitReferences(ty_unit);
   1081             zcu.deleteUnitCompileLogs(ty_unit);
   1082             if (zcu.failed_analysis.fetchSwapRemove(ty_unit)) |kv| {
   1083                 kv.value.destroy(gpa);
   1084             }
   1085             _ = zcu.transitive_failed_analysis.swapRemove(ty_unit);
   1086             ip.removeDependenciesForDepender(gpa, ty_unit);
   1087         }
   1088         try pt.addDependency(ty_unit, .{ .nav_val = nav_id });
   1089         if (new_failed) try zcu.transitive_failed_analysis.put(gpa, ty_unit, {});
   1090         if (ty_was_outdated) try zcu.markDependeeOutdated(.marked_po, .{ .nav_ty = nav_id });
   1091     }
   1092 
   1093     if (new_failed) return error.AnalysisFail;
   1094 }
   1095 
   1096 fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } {
   1097     const zcu = pt.zcu;
   1098     const ip = &zcu.intern_pool;
   1099     const comp = zcu.comp;
   1100     const gpa = comp.gpa;
   1101     const io = comp.io;
   1102 
   1103     const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
   1104     const old_nav = ip.getNav(nav_id);
   1105 
   1106     log.debug("analyzeNavVal {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1107 
   1108     const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
   1109     const file = zcu.fileByIndex(inst_resolved.file);
   1110     const zir = file.zir.?;
   1111     const zir_decl = zir.getDeclaration(inst_resolved.inst);
   1112 
   1113     try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
   1114     errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
   1115 
   1116     // If there's no type body, we are also resolving the type here.
   1117     if (zir_decl.type_body == null) {
   1118         try zcu.analysis_in_progress.putNoClobber(gpa, .wrap(.{ .nav_ty = nav_id }), {});
   1119     }
   1120     errdefer if (zir_decl.type_body == null) {
   1121         _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }));
   1122     };
   1123 
   1124     var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
   1125     defer analysis_arena.deinit();
   1126 
   1127     var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
   1128     defer comptime_err_ret_trace.deinit();
   1129 
   1130     var sema: Sema = .{
   1131         .pt = pt,
   1132         .gpa = gpa,
   1133         .arena = analysis_arena.allocator(),
   1134         .code = zir,
   1135         .owner = anal_unit,
   1136         .func_index = .none,
   1137         .func_is_naked = false,
   1138         .fn_ret_ty = .void,
   1139         .fn_ret_ty_ies = null,
   1140         .comptime_err_ret_trace = &comptime_err_ret_trace,
   1141     };
   1142     defer sema.deinit();
   1143 
   1144     // Every `Nav` declares a dependency on the source of the corresponding declaration.
   1145     try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
   1146 
   1147     // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
   1148     // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
   1149     // these references are known implicitly. See logic in `Zcu.resolveReferences`.
   1150 
   1151     var block: Sema.Block = .{
   1152         .parent = null,
   1153         .sema = &sema,
   1154         .namespace = old_nav.analysis.?.namespace,
   1155         .instructions = .{},
   1156         .inlining = null,
   1157         .comptime_reason = undefined, // set below
   1158         .src_base_inst = old_nav.analysis.?.zir_index,
   1159         .type_name_ctx = old_nav.fqn,
   1160     };
   1161     defer block.instructions.deinit(gpa);
   1162 
   1163     const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero });
   1164     const init_src = block.src(.{ .node_offset_var_decl_init = .zero });
   1165     const align_src = block.src(.{ .node_offset_var_decl_align = .zero });
   1166     const section_src = block.src(.{ .node_offset_var_decl_section = .zero });
   1167     const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero });
   1168 
   1169     block.comptime_reason = .{ .reason = .{
   1170         .src = init_src,
   1171         .r = .{ .simple = .container_var_init },
   1172     } };
   1173 
   1174     const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: {
   1175         // Since we have a type body, the type is resolved separately!
   1176         // Of course, we need to make sure we depend on it properly.
   1177         try sema.declareDependency(.{ .nav_ty = nav_id });
   1178         try pt.ensureNavTypeUpToDate(nav_id);
   1179         break :ty .fromInterned(ip.getNav(nav_id).typeOf(ip));
   1180     } else null;
   1181 
   1182     const final_val: ?Value = if (zir_decl.value_body) |value_body| val: {
   1183         if (maybe_ty) |ty| {
   1184             // Put the resolved type into `inst_map` to be used as the result type of the init.
   1185             try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
   1186             sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(ty.toIntern()));
   1187             const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
   1188             assert(sema.inst_map.remove(inst_resolved.inst));
   1189 
   1190             const result_ref = try sema.coerce(&block, ty, uncoerced_result_ref, init_src);
   1191             break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
   1192         } else {
   1193             // Just analyze the value; we have no type to offer.
   1194             const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
   1195             break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
   1196         }
   1197     } else null;
   1198 
   1199     const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu);
   1200 
   1201     // First, we must resolve the declaration's type. To do this, we analyze the type body if available,
   1202     // or otherwise, we analyze the value body, populating `early_val` in the process.
   1203 
   1204     const is_const = is_const: switch (zir_decl.kind) {
   1205         .@"comptime" => unreachable, // this is not a Nav
   1206         .unnamed_test, .@"test", .decltest => {
   1207             assert(nav_ty.zigTypeTag(zcu) == .@"fn");
   1208             break :is_const true;
   1209         },
   1210         .@"const" => true,
   1211         .@"var" => {
   1212             try sema.validateVarType(
   1213                 &block,
   1214                 if (zir_decl.type_body != null) ty_src else init_src,
   1215                 nav_ty,
   1216                 zir_decl.linkage == .@"extern",
   1217             );
   1218             break :is_const false;
   1219         },
   1220     };
   1221 
   1222     // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
   1223     // the full pointer type of this declaration.
   1224 
   1225     const modifiers: Sema.NavPtrModifiers = if (zir_decl.type_body != null) m: {
   1226         // `analyzeNavType` (from the `ensureNavTypeUpToDate` call above) has already populated this data into
   1227         // the `Nav`. Load the new one, and pull the modifiers out.
   1228         switch (ip.getNav(nav_id).status) {
   1229             .unresolved => unreachable, // `analyzeNavType` will never leave us in this state
   1230             inline .type_resolved, .fully_resolved => |r| break :m .{
   1231                 .alignment = r.alignment,
   1232                 .@"linksection" = r.@"linksection",
   1233                 .@"addrspace" = r.@"addrspace",
   1234             },
   1235         }
   1236     } else m: {
   1237         // `analyzeNavType` is essentially a stub which calls us. We are responsible for resolving this data.
   1238         break :m try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, nav_ty);
   1239     };
   1240 
   1241     // Lastly, we must figure out the actual interned value to store to the `Nav`.
   1242     // This isn't necessarily the same as `final_val`!
   1243 
   1244     const nav_val: Value = switch (zir_decl.linkage) {
   1245         .normal, .@"export" => switch (zir_decl.kind) {
   1246             .@"var" => .fromInterned(try pt.intern(.{ .variable = .{
   1247                 .ty = nav_ty.toIntern(),
   1248                 .init = final_val.?.toIntern(),
   1249                 .owner_nav = nav_id,
   1250                 .is_threadlocal = zir_decl.is_threadlocal,
   1251             } })),
   1252             else => final_val.?,
   1253         },
   1254         .@"extern" => val: {
   1255             assert(final_val == null); // extern decls do not have a value body
   1256             const lib_name: ?[]const u8 = if (zir_decl.lib_name != .empty) l: {
   1257                 break :l zir.nullTerminatedString(zir_decl.lib_name);
   1258             } else null;
   1259             if (lib_name) |l| {
   1260                 const lib_name_src = block.src(.{ .node_offset_lib_name = .zero });
   1261                 try sema.handleExternLibName(&block, lib_name_src, l);
   1262             }
   1263             break :val .fromInterned(try pt.getExtern(.{
   1264                 .name = old_nav.name,
   1265                 .ty = nav_ty.toIntern(),
   1266                 .lib_name = try ip.getOrPutStringOpt(gpa, io, pt.tid, lib_name, .no_embedded_nulls),
   1267                 .is_threadlocal = zir_decl.is_threadlocal,
   1268                 .linkage = .strong,
   1269                 .visibility = .default,
   1270                 .is_dll_import = false,
   1271                 .relocation = .any,
   1272                 .is_const = is_const,
   1273                 .alignment = modifiers.alignment,
   1274                 .@"addrspace" = modifiers.@"addrspace",
   1275                 .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction
   1276                 .owner_nav = undefined, // ignored by `getExtern`
   1277                 .source = .syntax,
   1278             }));
   1279         },
   1280     };
   1281 
   1282     switch (nav_val.toIntern()) {
   1283         .unreachable_value => unreachable, // assertion failure
   1284         else => {},
   1285     }
   1286 
   1287     // This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type,
   1288     // this resolves the type `type` (which needs no resolution), not the struct itself.
   1289     try nav_ty.resolveLayout(pt);
   1290 
   1291     const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
   1292         .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
   1293         .variable => |v| .{ v.owner_nav == nav_id, false },
   1294         .@"extern" => |e| .{
   1295             false,
   1296             Type.fromInterned(e.ty).zigTypeTag(zcu) == .@"fn" and zir_decl.linkage == .@"extern",
   1297         },
   1298         else => .{ true, false },
   1299     };
   1300 
   1301     if (is_owned_fn) {
   1302         // linksection etc are legal, except some targets do not support function alignment.
   1303         if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) {
   1304             return sema.fail(&block, align_src, "target does not support function alignment", .{});
   1305         }
   1306     } else if (try nav_ty.comptimeOnlySema(pt)) {
   1307         // alignment, linksection, addrspace annotations are not allowed for comptime-only types.
   1308         const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) {
   1309             .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
   1310             else => "comptime-only type",
   1311         };
   1312         if (zir_decl.align_body != null) {
   1313             return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
   1314         }
   1315         if (zir_decl.linksection_body != null) {
   1316             return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
   1317         }
   1318         if (zir_decl.addrspace_body != null) {
   1319             return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
   1320         }
   1321     }
   1322 
   1323     ip.resolveNavValue(io, nav_id, .{
   1324         .val = nav_val.toIntern(),
   1325         .is_const = is_const,
   1326         .alignment = modifiers.alignment,
   1327         .@"linksection" = modifiers.@"linksection",
   1328         .@"addrspace" = modifiers.@"addrspace",
   1329     });
   1330 
   1331     // Mark the unit as completed before evaluating the export!
   1332     assert(zcu.analysis_in_progress.swapRemove(anal_unit));
   1333     if (zir_decl.type_body == null) {
   1334         assert(zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id })));
   1335     }
   1336 
   1337     if (zir_decl.linkage == .@"export") {
   1338         const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) });
   1339         const name_slice = zir.nullTerminatedString(zir_decl.name);
   1340         const name_ip = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls);
   1341         try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id);
   1342     }
   1343 
   1344     try sema.flushExports();
   1345 
   1346     queue_codegen: {
   1347         if (!queue_linker_work) break :queue_codegen;
   1348 
   1349         if (!try nav_ty.hasRuntimeBitsSema(pt)) {
   1350             if (zcu.comp.config.use_llvm) break :queue_codegen;
   1351             if (file.mod.?.strip) break :queue_codegen;
   1352         }
   1353 
   1354         // This job depends on any resolve_type_fully jobs queued up before it.
   1355         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   1356         try zcu.comp.queueJob(.{ .link_nav = nav_id });
   1357     }
   1358 
   1359     switch (old_nav.status) {
   1360         .unresolved, .type_resolved => return .{ .val_changed = true },
   1361         .fully_resolved => |old| return .{ .val_changed = old.val != nav_val.toIntern() },
   1362     }
   1363 }
   1364 
   1365 pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
   1366     const tracy = trace(@src());
   1367     defer tracy.end();
   1368 
   1369     const zcu = pt.zcu;
   1370     const gpa = zcu.gpa;
   1371     const ip = &zcu.intern_pool;
   1372 
   1373     const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
   1374     const nav = ip.getNav(nav_id);
   1375 
   1376     log.debug("ensureNavTypeUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1377 
   1378     assert(!zcu.analysis_in_progress.contains(anal_unit));
   1379 
   1380     const type_resolved_by_value: bool = from_val: {
   1381         const analysis = nav.analysis orelse break :from_val false;
   1382         const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false;
   1383         const file = zcu.fileByIndex(inst_resolved.file);
   1384         const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst);
   1385         break :from_val zir_decl.type_body == null;
   1386     };
   1387     if (type_resolved_by_value) {
   1388         // Logic at the end of `ensureNavValUpToDate` is directly responsible for populating our state.
   1389         return pt.ensureNavValUpToDate(nav_id);
   1390     }
   1391 
   1392     // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the
   1393     // status is `.unresolved`, which indicates that the value is outdated because it has *never*
   1394     // been analyzed so far.
   1395     //
   1396     // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
   1397     // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
   1398     // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
   1399     // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
   1400 
   1401     const was_outdated = zcu.outdated.swapRemove(anal_unit) or
   1402         zcu.potentially_outdated.swapRemove(anal_unit);
   1403 
   1404     const prev_failed = zcu.failed_analysis.contains(anal_unit) or
   1405         zcu.transitive_failed_analysis.contains(anal_unit);
   1406 
   1407     if (was_outdated) {
   1408         dev.check(.incremental);
   1409         _ = zcu.outdated_ready.swapRemove(anal_unit);
   1410         zcu.deleteUnitExports(anal_unit);
   1411         zcu.deleteUnitReferences(anal_unit);
   1412         zcu.deleteUnitCompileLogs(anal_unit);
   1413         if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
   1414             kv.value.destroy(gpa);
   1415         }
   1416         _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
   1417         ip.removeDependenciesForDepender(gpa, anal_unit);
   1418     } else {
   1419         // We can trust the current information about this unit.
   1420         if (prev_failed) return error.AnalysisFail;
   1421         switch (nav.status) {
   1422             .unresolved => {},
   1423             .type_resolved, .fully_resolved => return,
   1424         }
   1425     }
   1426 
   1427     if (zcu.comp.debugIncremental()) {
   1428         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
   1429         info.last_update_gen = zcu.generation;
   1430         info.deps.clearRetainingCapacity();
   1431     }
   1432 
   1433     const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip));
   1434     defer unit_tracking.end(zcu);
   1435 
   1436     const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {
   1437         break :res .{
   1438             // If the unit has gone from failed to success, we still need to invalidate the dependencies.
   1439             result.type_changed or prev_failed,
   1440             false,
   1441         };
   1442     } else |err| switch (err) {
   1443         error.AnalysisFail => res: {
   1444             if (!zcu.failed_analysis.contains(anal_unit)) {
   1445                 // If this unit caused the error, it would have an entry in `failed_analysis`.
   1446                 // Since it does not, this must be a transitive failure.
   1447                 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
   1448                 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1449             }
   1450             break :res .{ !prev_failed, true };
   1451         },
   1452         error.OutOfMemory => {
   1453             // TODO: it's unclear how to gracefully handle this.
   1454             // To report the error cleanly, we need to add a message to `failed_analysis` and a
   1455             // corresponding entry to `retryable_failures`; but either of these things is quite
   1456             // likely to OOM at this point.
   1457             // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
   1458             // for reporting OOM errors without allocating.
   1459             return error.OutOfMemory;
   1460         },
   1461         error.Canceled => |e| return e,
   1462         error.ComptimeReturn => unreachable,
   1463         error.ComptimeBreak => unreachable,
   1464     };
   1465 
   1466     if (was_outdated) {
   1467         const dependee: InternPool.Dependee = .{ .nav_ty = nav_id };
   1468         if (invalidate_type) {
   1469             // This dependency was marked as PO, meaning dependees were waiting
   1470             // on its analysis result, and it has turned out to be outdated.
   1471             // Update dependees accordingly.
   1472             try zcu.markDependeeOutdated(.marked_po, dependee);
   1473         } else {
   1474             // This dependency was previously PO, but turned out to be up-to-date.
   1475             // We do not need to queue successive analysis.
   1476             try zcu.markPoDependeeUpToDate(dependee);
   1477         }
   1478     }
   1479 
   1480     if (new_failed) return error.AnalysisFail;
   1481 }
   1482 
   1483 fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } {
   1484     const zcu = pt.zcu;
   1485     const comp = zcu.comp;
   1486     const gpa = comp.gpa;
   1487     const io = comp.io;
   1488     const ip = &zcu.intern_pool;
   1489 
   1490     const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
   1491     const old_nav = ip.getNav(nav_id);
   1492 
   1493     log.debug("analyzeNavType {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1494 
   1495     const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
   1496     const file = zcu.fileByIndex(inst_resolved.file);
   1497     const zir = file.zir.?;
   1498 
   1499     try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
   1500     defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
   1501 
   1502     const zir_decl = zir.getDeclaration(inst_resolved.inst);
   1503     const type_body = zir_decl.type_body.?;
   1504 
   1505     var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
   1506     defer analysis_arena.deinit();
   1507 
   1508     var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
   1509     defer comptime_err_ret_trace.deinit();
   1510 
   1511     var sema: Sema = .{
   1512         .pt = pt,
   1513         .gpa = gpa,
   1514         .arena = analysis_arena.allocator(),
   1515         .code = zir,
   1516         .owner = anal_unit,
   1517         .func_index = .none,
   1518         .func_is_naked = false,
   1519         .fn_ret_ty = .void,
   1520         .fn_ret_ty_ies = null,
   1521         .comptime_err_ret_trace = &comptime_err_ret_trace,
   1522     };
   1523     defer sema.deinit();
   1524 
   1525     // Every `Nav` declares a dependency on the source of the corresponding declaration.
   1526     try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
   1527 
   1528     // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
   1529     // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
   1530     // these references are known implicitly. See logic in `Zcu.resolveReferences`.
   1531 
   1532     var block: Sema.Block = .{
   1533         .parent = null,
   1534         .sema = &sema,
   1535         .namespace = old_nav.analysis.?.namespace,
   1536         .instructions = .{},
   1537         .inlining = null,
   1538         .comptime_reason = undefined, // set below
   1539         .src_base_inst = old_nav.analysis.?.zir_index,
   1540         .type_name_ctx = old_nav.fqn,
   1541     };
   1542     defer block.instructions.deinit(gpa);
   1543 
   1544     const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero });
   1545 
   1546     block.comptime_reason = .{ .reason = .{
   1547         .src = ty_src,
   1548         .r = .{ .simple = .type },
   1549     } };
   1550 
   1551     const resolved_ty: Type = ty: {
   1552         const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
   1553         const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
   1554         break :ty .fromInterned(type_ref.toInterned().?);
   1555     };
   1556 
   1557     try resolved_ty.resolveLayout(pt);
   1558 
   1559     // In the case where the type is specified, this function is also responsible for resolving
   1560     // the pointer modifiers, i.e. alignment, linksection, addrspace.
   1561     const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty);
   1562 
   1563     const is_const = switch (zir_decl.kind) {
   1564         .@"comptime" => unreachable,
   1565         .unnamed_test, .@"test", .decltest, .@"const" => true,
   1566         .@"var" => false,
   1567     };
   1568 
   1569     const is_extern_decl = zir_decl.linkage == .@"extern";
   1570 
   1571     // Now for the question of the day: are the type and modifiers the same as before?
   1572     // If they are, then we should actually keep the `Nav` as `fully_resolved` if it currently is.
   1573     // That's because `analyzeNavVal` will later want to look at the resolved value to figure out
   1574     // whether it's changed: if we threw that data away now, it would have to assume that the value
   1575     // had changed, potentially spinning off loads of unnecessary re-analysis!
   1576     const changed = switch (old_nav.status) {
   1577         .unresolved => true,
   1578         .type_resolved => |r| r.type != resolved_ty.toIntern() or
   1579             r.alignment != modifiers.alignment or
   1580             r.@"linksection" != modifiers.@"linksection" or
   1581             r.@"addrspace" != modifiers.@"addrspace" or
   1582             r.is_const != is_const or
   1583             r.is_extern_decl != is_extern_decl,
   1584         .fully_resolved => |r| ip.typeOf(r.val) != resolved_ty.toIntern() or
   1585             r.alignment != modifiers.alignment or
   1586             r.@"linksection" != modifiers.@"linksection" or
   1587             r.@"addrspace" != modifiers.@"addrspace" or
   1588             r.is_const != is_const or
   1589             (old_nav.getExtern(ip) != null) != is_extern_decl,
   1590     };
   1591 
   1592     if (!changed) return .{ .type_changed = false };
   1593 
   1594     ip.resolveNavType(io, nav_id, .{
   1595         .type = resolved_ty.toIntern(),
   1596         .is_const = is_const,
   1597         .alignment = modifiers.alignment,
   1598         .@"linksection" = modifiers.@"linksection",
   1599         .@"addrspace" = modifiers.@"addrspace",
   1600         .is_threadlocal = zir_decl.is_threadlocal,
   1601         .is_extern_decl = is_extern_decl,
   1602     });
   1603 
   1604     return .{ .type_changed = true };
   1605 }
   1606 
   1607 pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!void {
   1608     dev.check(.sema);
   1609 
   1610     const tracy = trace(@src());
   1611     defer tracy.end();
   1612 
   1613     const zcu = pt.zcu;
   1614     const gpa = zcu.gpa;
   1615     const ip = &zcu.intern_pool;
   1616 
   1617     _ = zcu.func_body_analysis_queued.swapRemove(func_index);
   1618 
   1619     const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
   1620 
   1621     log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1622 
   1623     assert(!zcu.analysis_in_progress.contains(anal_unit));
   1624 
   1625     const func = zcu.funcInfo(func_index);
   1626 
   1627     assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one
   1628 
   1629     const was_outdated = zcu.outdated.swapRemove(anal_unit) or
   1630         zcu.potentially_outdated.swapRemove(anal_unit);
   1631 
   1632     const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
   1633 
   1634     if (was_outdated) {
   1635         dev.check(.incremental);
   1636         _ = zcu.outdated_ready.swapRemove(anal_unit);
   1637         zcu.deleteUnitExports(anal_unit);
   1638         zcu.deleteUnitReferences(anal_unit);
   1639         zcu.deleteUnitCompileLogs(anal_unit);
   1640         if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
   1641             kv.value.destroy(gpa);
   1642         }
   1643         _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
   1644     } else {
   1645         // We can trust the current information about this function.
   1646         if (prev_failed) {
   1647             return error.AnalysisFail;
   1648         }
   1649         if (func.analysisUnordered(ip).is_analyzed) return;
   1650     }
   1651 
   1652     if (zcu.comp.debugIncremental()) {
   1653         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
   1654         info.last_update_gen = zcu.generation;
   1655         info.deps.clearRetainingCapacity();
   1656     }
   1657 
   1658     const owner_nav = ip.getNav(func.owner_nav);
   1659     const unit_tracking = zcu.trackUnitSema(
   1660         owner_nav.fqn.toSlice(ip),
   1661         owner_nav.srcInst(ip),
   1662     );
   1663     defer unit_tracking.end(zcu);
   1664 
   1665     const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result|
   1666         .{ prev_failed or result.ies_outdated, false }
   1667     else |err| switch (err) {
   1668         error.AnalysisFail => res: {
   1669             if (!zcu.failed_analysis.contains(anal_unit)) {
   1670                 // If this function caused the error, it would have an entry in `failed_analysis`.
   1671                 // Since it does not, this must be a transitive failure.
   1672                 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
   1673                 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1674             }
   1675             // We consider the IES to be outdated if the function previously succeeded analysis; in this case,
   1676             // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting
   1677             // a different error later (which may now be invalid).
   1678             break :res .{ !prev_failed, true };
   1679         },
   1680         error.OutOfMemory => {
   1681             // TODO: it's unclear how to gracefully handle this.
   1682             // To report the error cleanly, we need to add a message to `failed_analysis` and a
   1683             // corresponding entry to `retryable_failures`; but either of these things is quite
   1684             // likely to OOM at this point.
   1685             // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
   1686             // for reporting OOM errors without allocating.
   1687             return error.OutOfMemory;
   1688         },
   1689         error.Canceled => |e| return e,
   1690     };
   1691 
   1692     if (was_outdated) {
   1693         if (ies_outdated) {
   1694             try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
   1695         } else {
   1696             try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
   1697         }
   1698     }
   1699 
   1700     if (new_failed) return error.AnalysisFail;
   1701 }
   1702 
   1703 fn analyzeFuncBody(
   1704     pt: Zcu.PerThread,
   1705     func_index: InternPool.Index,
   1706 ) Zcu.SemaError!struct { ies_outdated: bool } {
   1707     const zcu = pt.zcu;
   1708     const gpa = zcu.gpa;
   1709     const ip = &zcu.intern_pool;
   1710 
   1711     const func = zcu.funcInfo(func_index);
   1712     const anal_unit = AnalUnit.wrap(.{ .func = func_index });
   1713 
   1714     // Make sure that this function is still owned by the same `Nav`. Otherwise, analyzing
   1715     // it would be a waste of time in the best case, and could cause codegen to give bogus
   1716     // results in the worst case.
   1717 
   1718     if (func.generic_owner == .none) {
   1719         // Among another things, this ensures that the function's `zir_body_inst` is correct.
   1720         try pt.ensureNavValUpToDate(func.owner_nav);
   1721         if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) {
   1722             // This function is no longer referenced! There's no point in re-analyzing it.
   1723             // Just mark a transitive failure and move on.
   1724             return error.AnalysisFail;
   1725         }
   1726     } else {
   1727         const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
   1728         // Among another things, this ensures that the function's `zir_body_inst` is correct.
   1729         try pt.ensureNavValUpToDate(go_nav);
   1730         if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) {
   1731             // The generic owner is no longer referenced, so this function is also unreferenced.
   1732             // There's no point in re-analyzing it. Just mark a transitive failure and move on.
   1733             return error.AnalysisFail;
   1734         }
   1735     }
   1736 
   1737     // We'll want to remember what the IES used to be before the update for
   1738     // dependency invalidation purposes.
   1739     const old_resolved_ies = if (func.analysisUnordered(ip).inferred_error_set)
   1740         func.resolvedErrorSetUnordered(ip)
   1741     else
   1742         .none;
   1743 
   1744     log.debug("analyze and generate fn body {f}", .{zcu.fmtAnalUnit(anal_unit)});
   1745 
   1746     var air = try pt.analyzeFnBodyInner(func_index);
   1747     errdefer air.deinit(gpa);
   1748 
   1749     const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or
   1750         func.resolvedErrorSetUnordered(ip) != old_resolved_ies;
   1751 
   1752     const comp = zcu.comp;
   1753 
   1754     const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
   1755     const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null);
   1756 
   1757     if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
   1758         air.deinit(gpa);
   1759         return .{ .ies_outdated = ies_outdated };
   1760     }
   1761 
   1762     // This job depends on any resolve_type_fully jobs queued up before it.
   1763     zcu.codegen_prog_node.increaseEstimatedTotalItems(1);
   1764     comp.link_prog_node.increaseEstimatedTotalItems(1);
   1765     try comp.queueJob(.{ .codegen_func = .{
   1766         .func = func_index,
   1767         .air = air,
   1768     } });
   1769 
   1770     return .{ .ies_outdated = ies_outdated };
   1771 }
   1772 
   1773 pub fn semaMod(pt: Zcu.PerThread, mod: *Module) !void {
   1774     dev.check(.sema);
   1775     const file_index = pt.zcu.module_roots.get(mod).?.unwrap().?;
   1776     const root_type = pt.zcu.fileRootType(file_index);
   1777     if (root_type == .none) {
   1778         return pt.semaFile(file_index);
   1779     }
   1780 }
   1781 
   1782 fn createFileRootStruct(
   1783     pt: Zcu.PerThread,
   1784     file_index: Zcu.File.Index,
   1785     namespace_index: Zcu.Namespace.Index,
   1786     replace_existing: bool,
   1787 ) Allocator.Error!InternPool.Index {
   1788     const zcu = pt.zcu;
   1789     const gpa = zcu.gpa;
   1790     const io = zcu.comp.io;
   1791     const ip = &zcu.intern_pool;
   1792     const file = zcu.fileByIndex(file_index);
   1793     const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
   1794     assert(extended.opcode == .struct_decl);
   1795     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   1796     assert(!small.has_captures_len);
   1797     assert(!small.has_backing_int);
   1798     assert(small.layout == .auto);
   1799     var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
   1800     const fields_len = if (small.has_fields_len) blk: {
   1801         const fields_len = file.zir.?.extra[extra_index];
   1802         extra_index += 1;
   1803         break :blk fields_len;
   1804     } else 0;
   1805     const decls_len = if (small.has_decls_len) blk: {
   1806         const decls_len = file.zir.?.extra[extra_index];
   1807         extra_index += 1;
   1808         break :blk decls_len;
   1809     } else 0;
   1810     const decls = file.zir.?.bodySlice(extra_index, decls_len);
   1811     extra_index += decls_len;
   1812 
   1813     const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{
   1814         .file = file_index,
   1815         .inst = .main_struct_inst,
   1816     });
   1817     const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
   1818         .layout = .auto,
   1819         .fields_len = fields_len,
   1820         .known_non_opv = small.known_non_opv,
   1821         .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
   1822         .any_comptime_fields = small.any_comptime_fields,
   1823         .any_default_inits = small.any_default_inits,
   1824         .inits_resolved = false,
   1825         .any_aligned_fields = small.any_aligned_fields,
   1826         .key = .{ .declared = .{
   1827             .zir_index = tracked_inst,
   1828             .captures = &.{},
   1829         } },
   1830     }, replace_existing)) {
   1831         .existing => unreachable, // we wouldn't be analysing the file root if this type existed
   1832         .wip => |wip| wip,
   1833     };
   1834     errdefer wip_ty.cancel(ip, pt.tid);
   1835 
   1836     wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none);
   1837     ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
   1838 
   1839     if (zcu.comp.config.incremental) {
   1840         try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
   1841     }
   1842 
   1843     try pt.scanNamespace(namespace_index, decls);
   1844     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   1845     codegen_type: {
   1846         if (file.mod.?.strip) break :codegen_type;
   1847         // This job depends on any resolve_type_fully jobs queued up before it.
   1848         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   1849         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   1850     }
   1851     zcu.setFileRootType(file_index, wip_ty.index);
   1852     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   1853     return wip_ty.finish(ip, namespace_index);
   1854 }
   1855 
   1856 /// Re-scan the namespace of a file's root struct type on an incremental update.
   1857 /// The file must have successfully populated ZIR.
   1858 /// If the file's root struct type is not populated (the file is unreferenced), nothing is done.
   1859 /// This is called by `updateZirRefs` for all updated files before the main work loop.
   1860 /// This function does not perform any semantic analysis.
   1861 fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
   1862     const zcu = pt.zcu;
   1863 
   1864     const file = zcu.fileByIndex(file_index);
   1865     const file_root_type = zcu.fileRootType(file_index);
   1866     if (file_root_type == .none) return;
   1867 
   1868     log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{
   1869         file.mod.?.fully_qualified_name,
   1870         file.sub_file_path,
   1871     });
   1872 
   1873     const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
   1874     const decls = decls: {
   1875         const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
   1876         const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   1877 
   1878         var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
   1879         extra_index += @intFromBool(small.has_fields_len);
   1880         const decls_len = if (small.has_decls_len) blk: {
   1881             const decls_len = file.zir.?.extra[extra_index];
   1882             extra_index += 1;
   1883             break :blk decls_len;
   1884         } else 0;
   1885         break :decls file.zir.?.bodySlice(extra_index, decls_len);
   1886     };
   1887     try pt.scanNamespace(namespace_index, decls);
   1888     zcu.namespacePtr(namespace_index).generation = zcu.generation;
   1889 }
   1890 
   1891 fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
   1892     const tracy = trace(@src());
   1893     defer tracy.end();
   1894 
   1895     const zcu = pt.zcu;
   1896     const file = zcu.fileByIndex(file_index);
   1897     assert(file.getMode() == .zig);
   1898     assert(zcu.fileRootType(file_index) == .none);
   1899 
   1900     assert(file.zir != null);
   1901 
   1902     const new_namespace_index = try pt.createNamespace(.{
   1903         .parent = .none,
   1904         .owner_type = undefined, // set in `createFileRootStruct`
   1905         .file_scope = file_index,
   1906         .generation = zcu.generation,
   1907     });
   1908     const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
   1909     errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
   1910 
   1911     if (zcu.comp.time_report) |*tr| {
   1912         tr.stats.n_imported_files += 1;
   1913     }
   1914 }
   1915 
   1916 /// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is
   1917 /// then responsible for queueing a new AstGen job for the new file.
   1918 /// Assumes that `comp.mutex` is NOT locked. It will be locked by this function where necessary.
   1919 pub fn discoverImport(
   1920     pt: Zcu.PerThread,
   1921     importer_path: Compilation.Path,
   1922     import_string: []const u8,
   1923 ) Allocator.Error!union(enum) {
   1924     module,
   1925     existing_file: Zcu.File.Index,
   1926     new_file: struct {
   1927         index: Zcu.File.Index,
   1928         file: *Zcu.File,
   1929     },
   1930 } {
   1931     const zcu = pt.zcu;
   1932     const comp = zcu.comp;
   1933     const io = comp.io;
   1934     const gpa = comp.gpa;
   1935 
   1936     if (!mem.endsWith(u8, import_string, ".zig") and !mem.endsWith(u8, import_string, ".zon")) {
   1937         return .module;
   1938     }
   1939 
   1940     const new_path = try importer_path.upJoin(gpa, zcu.comp.dirs, import_string);
   1941     errdefer new_path.deinit(gpa);
   1942 
   1943     // We're about to do a GOP on `import_table`, so we need the mutex.
   1944     comp.mutex.lockUncancelable(io);
   1945     defer comp.mutex.unlock(io);
   1946 
   1947     const gop = try zcu.import_table.getOrPutAdapted(gpa, new_path, Zcu.ImportTableAdapter{ .zcu = zcu });
   1948     errdefer _ = zcu.import_table.pop();
   1949     if (gop.found_existing) {
   1950         new_path.deinit(gpa); // we didn't need it for `File.path`
   1951         return .{ .existing_file = gop.key_ptr.* };
   1952     }
   1953 
   1954     zcu.import_table.lockPointers();
   1955     defer zcu.import_table.unlockPointers();
   1956 
   1957     const new_file = try gpa.create(Zcu.File);
   1958     errdefer gpa.destroy(new_file);
   1959 
   1960     const new_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
   1961         .bin_digest = new_path.digest(),
   1962         .file = new_file,
   1963         .root_type = .none,
   1964     });
   1965     errdefer comptime unreachable; // because we don't remove the file from the internpool
   1966 
   1967     gop.key_ptr.* = new_file_index;
   1968     new_file.* = .{
   1969         .status = .never_loaded,
   1970         .path = new_path,
   1971         .stat = undefined,
   1972         .is_builtin = false,
   1973         .source = null,
   1974         .tree = null,
   1975         .zir = null,
   1976         .zoir = null,
   1977         .mod = null,
   1978         .sub_file_path = undefined,
   1979         .module_changed = false,
   1980         .prev_zir = null,
   1981         .zoir_invalidated = false,
   1982     };
   1983 
   1984     return .{ .new_file = .{
   1985         .index = new_file_index,
   1986         .file = new_file,
   1987     } };
   1988 }
   1989 
   1990 pub fn doImport(
   1991     pt: Zcu.PerThread,
   1992     /// This file must have its `mod` populated.
   1993     importer: *Zcu.File,
   1994     import_string: []const u8,
   1995 ) error{
   1996     OutOfMemory,
   1997     ModuleNotFound,
   1998     IllegalZigImport,
   1999 }!struct {
   2000     file: Zcu.File.Index,
   2001     module_root: ?*Module,
   2002 } {
   2003     const zcu = pt.zcu;
   2004     const gpa = zcu.gpa;
   2005     const imported_mod: ?*Module = m: {
   2006         if (mem.eql(u8, import_string, "std")) break :m zcu.std_mod;
   2007         if (mem.eql(u8, import_string, "root")) break :m zcu.root_mod;
   2008         if (mem.eql(u8, import_string, "builtin")) {
   2009             const opts = importer.mod.?.getBuiltinOptions(zcu.comp.config);
   2010             break :m zcu.builtin_modules.get(opts.hash()).?;
   2011         }
   2012         break :m importer.mod.?.deps.get(import_string);
   2013     };
   2014     if (imported_mod) |mod| {
   2015         if (zcu.module_roots.get(mod).?.unwrap()) |file_index| {
   2016             return .{
   2017                 .file = file_index,
   2018                 .module_root = mod,
   2019             };
   2020         }
   2021     }
   2022     if (!std.mem.endsWith(u8, import_string, ".zig") and
   2023         !std.mem.endsWith(u8, import_string, ".zon"))
   2024     {
   2025         return error.ModuleNotFound;
   2026     }
   2027     const path = try importer.path.upJoin(gpa, zcu.comp.dirs, import_string);
   2028     defer path.deinit(gpa);
   2029     if (try path.isIllegalZigImport(gpa, zcu.comp.dirs)) {
   2030         return error.IllegalZigImport;
   2031     }
   2032     return .{
   2033         .file = zcu.import_table.getKeyAdapted(path, Zcu.ImportTableAdapter{ .zcu = zcu }).?,
   2034         .module_root = null,
   2035     };
   2036 }
   2037 /// This is called once during `Compilation.create` and never again. "builtin" modules don't yet
   2038 /// exist, so are not added to `module_roots` here. They must be added when they are created.
   2039 pub fn populateModuleRootTable(pt: Zcu.PerThread) error{
   2040     OutOfMemory,
   2041     /// One of the specified modules had its root source file at an illegal path.
   2042     IllegalZigImport,
   2043 }!void {
   2044     const zcu = pt.zcu;
   2045     const comp = zcu.comp;
   2046     const gpa = comp.gpa;
   2047     const io = comp.io;
   2048 
   2049     // We'll initially add [mod, undefined] pairs, and when we reach the pair while
   2050     // iterating, rewrite the undefined value.
   2051     const roots = &zcu.module_roots;
   2052     roots.clearRetainingCapacity();
   2053 
   2054     // Start with:
   2055     // * `std_mod`, which is the main root of analysis
   2056     // * `root_mod`, which is `@import("root")`
   2057     // * `main_mod`, which is a special analysis root in tests (and otherwise equal to `root_mod`)
   2058     // All other modules will be found by traversing their dependency tables.
   2059     try roots.ensureTotalCapacity(gpa, 3);
   2060     roots.putAssumeCapacity(zcu.std_mod, undefined);
   2061     roots.putAssumeCapacity(zcu.root_mod, undefined);
   2062     roots.putAssumeCapacity(zcu.main_mod, undefined);
   2063     var i: usize = 0;
   2064     while (i < roots.count()) {
   2065         const mod = roots.keys()[i];
   2066         try roots.ensureUnusedCapacity(gpa, mod.deps.count());
   2067         for (mod.deps.values()) |dep| {
   2068             const gop = roots.getOrPutAssumeCapacity(dep);
   2069             _ = gop; // we want to leave the value undefined if it was added
   2070         }
   2071 
   2072         const root_file_out = &roots.values()[i];
   2073         roots.lockPointers();
   2074         defer roots.unlockPointers();
   2075 
   2076         i += 1;
   2077 
   2078         if (Zcu.File.modeFromPath(mod.root_src_path) == null) {
   2079             root_file_out.* = .none;
   2080             continue;
   2081         }
   2082 
   2083         const path = try mod.root.join(gpa, zcu.comp.dirs, mod.root_src_path);
   2084         errdefer path.deinit(gpa);
   2085 
   2086         if (try path.isIllegalZigImport(gpa, zcu.comp.dirs)) {
   2087             return error.IllegalZigImport;
   2088         }
   2089 
   2090         const gop = try zcu.import_table.getOrPutAdapted(gpa, path, Zcu.ImportTableAdapter{ .zcu = zcu });
   2091         errdefer _ = zcu.import_table.pop();
   2092 
   2093         if (gop.found_existing) {
   2094             path.deinit(gpa);
   2095             root_file_out.* = gop.key_ptr.*.toOptional();
   2096             continue;
   2097         }
   2098 
   2099         zcu.import_table.lockPointers();
   2100         defer zcu.import_table.unlockPointers();
   2101 
   2102         const new_file = try gpa.create(Zcu.File);
   2103         errdefer gpa.destroy(new_file);
   2104 
   2105         const new_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
   2106             .bin_digest = path.digest(),
   2107             .file = new_file,
   2108             .root_type = .none,
   2109         });
   2110         errdefer comptime unreachable; // because we don't remove the file from the internpool
   2111 
   2112         gop.key_ptr.* = new_file_index;
   2113         root_file_out.* = new_file_index.toOptional();
   2114         new_file.* = .{
   2115             .status = .never_loaded,
   2116             .path = path,
   2117             .stat = undefined,
   2118             .is_builtin = false,
   2119             .source = null,
   2120             .tree = null,
   2121             .zir = null,
   2122             .zoir = null,
   2123             .mod = null,
   2124             .sub_file_path = undefined,
   2125             .module_changed = false,
   2126             .prev_zir = null,
   2127             .zoir_invalidated = false,
   2128         };
   2129     }
   2130 }
   2131 
   2132 /// Clears and re-populates `pt.zcu.alive_files`, and determines the module identity of every alive
   2133 /// file. If a file's module changes, its `module_changed` flag is set for `updateZirRefs` to see.
   2134 /// Also clears and re-populates `failed_imports` and `multi_module_err` based on the set of alive
   2135 /// files.
   2136 ///
   2137 /// Live files are also added as file system inputs if necessary.
   2138 ///
   2139 /// Returns whether there is any live file which is failed. Howewver, this function does *not*
   2140 /// modify `pt.zcu.skip_analysis_this_update`.
   2141 ///
   2142 /// If an error is returned, `pt.zcu.alive_files` might contain undefined values.
   2143 pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool {
   2144     const zcu = pt.zcu;
   2145     const comp = zcu.comp;
   2146     const gpa = zcu.gpa;
   2147 
   2148     var any_fatal_files = false;
   2149     zcu.multi_module_err = null;
   2150     zcu.failed_imports.clearRetainingCapacity();
   2151     zcu.alive_files.clearRetainingCapacity();
   2152 
   2153     // This function will iterate the keys of `alive_files`, adding new entries as it discovers
   2154     // imports. Once a file is in `alive_files`, it has its `mod` field up-to-date. If conflicting
   2155     // imports are discovered for a file, we will set `multi_module_err`. Crucially, this traversal
   2156     // is single-threaded, and depends only on the order of the imports map from AstGen, which makes
   2157     // its behavior (in terms of which multi module errors are discovered) entirely consistent in a
   2158     // multi-threaded environment (where things like file indices could differ between compiler runs).
   2159 
   2160     // The roots of our file liveness analysis will be the analysis roots.
   2161     const analysis_roots = zcu.analysisRoots();
   2162     try zcu.alive_files.ensureTotalCapacity(gpa, analysis_roots.len);
   2163     for (analysis_roots) |mod| {
   2164         const file_index = zcu.module_roots.get(mod).?.unwrap() orelse continue;
   2165         const file = zcu.fileByIndex(file_index);
   2166 
   2167         file.mod = mod;
   2168         file.sub_file_path = mod.root_src_path;
   2169 
   2170         zcu.alive_files.putAssumeCapacityNoClobber(file_index, .{ .analysis_root = mod });
   2171     }
   2172 
   2173     var live_check_idx: usize = 0;
   2174     while (live_check_idx < zcu.alive_files.count()) {
   2175         const file_idx = zcu.alive_files.keys()[live_check_idx];
   2176         const file = zcu.fileByIndex(file_idx);
   2177         live_check_idx += 1;
   2178 
   2179         switch (file.status) {
   2180             .never_loaded => unreachable, // everything reachable is loaded by the AstGen workers
   2181             .retryable_failure, .astgen_failure => any_fatal_files = true,
   2182             .success => {},
   2183         }
   2184 
   2185         try comp.appendFileSystemInput(file.path);
   2186 
   2187         switch (file.getMode()) {
   2188             .zig => {}, // continue to logic below
   2189             .zon => continue, // ZON can't import anything
   2190         }
   2191 
   2192         if (file.status != .success) continue; // ZIR not valid if there was a file failure
   2193 
   2194         const zir = file.zir.?;
   2195         const imports_index = zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
   2196         if (imports_index == 0) continue; // this Zig file has no imports
   2197         const extra = zir.extraData(Zir.Inst.Imports, imports_index);
   2198         var extra_index = extra.end;
   2199         try zcu.alive_files.ensureUnusedCapacity(gpa, extra.data.imports_len);
   2200         for (0..extra.data.imports_len) |_| {
   2201             const item = zir.extraData(Zir.Inst.Imports.Item, extra_index);
   2202             extra_index = item.end;
   2203             const import_path = zir.nullTerminatedString(item.data.name);
   2204 
   2205             if (std.mem.eql(u8, import_path, "builtin")) {
   2206                 // We've not necessarily generated builtin modules yet, so `doImport` could fail. Instead,
   2207                 // create the module here. Then, since we know that `builtin.zig` doesn't have an error and
   2208                 // has no imports other than 'std', we can just continue onto the next import.
   2209                 try pt.updateBuiltinModule(file.mod.?.getBuiltinOptions(comp.config));
   2210                 continue;
   2211             }
   2212 
   2213             const res = pt.doImport(file, import_path) catch |err| switch (err) {
   2214                 error.OutOfMemory => |e| return e,
   2215                 error.ModuleNotFound => {
   2216                     // It'd be nice if this were a file-level error, but allowing this turns out to
   2217                     // be quite important in practice, e.g. for optional dependencies whose import
   2218                     // is behind a comptime condition. So, the error here happens in `Sema` instead.
   2219                     continue;
   2220                 },
   2221                 error.IllegalZigImport => {
   2222                     try zcu.failed_imports.append(gpa, .{
   2223                         .file_index = file_idx,
   2224                         .import_string = item.data.name,
   2225                         .import_token = item.data.token,
   2226                         .kind = .illegal_zig_import,
   2227                     });
   2228                     continue;
   2229                 },
   2230             };
   2231 
   2232             // If the import was not of a module, we propagate our own module.
   2233             const imported_mod = res.module_root orelse file.mod.?;
   2234             const imported_file = zcu.fileByIndex(res.file);
   2235 
   2236             const imported_ref: Zcu.File.Reference = .{ .import = .{
   2237                 .importer = file_idx,
   2238                 .tok = item.data.token,
   2239                 .module = res.module_root,
   2240             } };
   2241 
   2242             const gop = zcu.alive_files.getOrPutAssumeCapacity(res.file);
   2243             if (gop.found_existing) {
   2244                 // This means `imported_file.mod` is already populated. If it doesn't match
   2245                 // `imported_mod`, then this file exists in multiple modules.
   2246                 if (imported_file.mod.? != imported_mod) {
   2247                     // We only report the first multi-module error we see. Thanks to this traversal
   2248                     // being deterministic, this doesn't raise consistency issues. Moreover, it's a
   2249                     // useful behavior; we know that this error can be reached *without* realising
   2250                     // that any other files are multi-module, so it's probably approximately where
   2251                     // the problem "begins". Any compilation with a multi-module file is likely to
   2252                     // have a huge number of them by transitive imports, so just reporting this one
   2253                     // hopefully keeps the error focused.
   2254                     zcu.multi_module_err = .{
   2255                         .file = file_idx,
   2256                         .modules = .{ imported_file.mod.?, imported_mod },
   2257                         .refs = .{ gop.value_ptr.*, imported_ref },
   2258                     };
   2259                     // If we discover a multi-module error, it's the only error which matters, and we
   2260                     // can't discern any useful information about the file's own imports; so just do
   2261                     // an early exit now we've populated `zcu.multi_module_err`.
   2262                     return any_fatal_files;
   2263                 }
   2264                 continue;
   2265             }
   2266             // We're the first thing we've found referencing `res.file`.
   2267             gop.value_ptr.* = imported_ref;
   2268             if (imported_file.mod) |m| {
   2269                 if (m == imported_mod) {
   2270                     // Great, the module and sub path are already populated correctly.
   2271                     continue;
   2272                 }
   2273             }
   2274             // We need to set the file's module, meaning we also need to compute its sub path.
   2275             // This string is externally managed and has a lifetime at least equal to the
   2276             // lifetime of `imported_file`. `null` means the file is outside its module root.
   2277             switch (imported_file.path.isNested(imported_mod.root)) {
   2278                 .yes => |sub_path| {
   2279                     if (imported_file.mod != null) {
   2280                         // There was a module from a previous update; instruct `updateZirRefs` to
   2281                         // invalidate everything.
   2282                         imported_file.module_changed = true;
   2283                     }
   2284                     imported_file.mod = imported_mod;
   2285                     imported_file.sub_file_path = sub_path;
   2286                 },
   2287                 .different_roots, .no => {
   2288                     try zcu.failed_imports.append(gpa, .{
   2289                         .file_index = file_idx,
   2290                         .import_string = item.data.name,
   2291                         .import_token = item.data.token,
   2292                         .kind = .file_outside_module_root,
   2293                     });
   2294                     _ = zcu.alive_files.pop(); // we failed to populate `mod`/`sub_file_path`
   2295                 },
   2296             }
   2297         }
   2298     }
   2299 
   2300     return any_fatal_files;
   2301 }
   2302 
   2303 /// Ensures that the `@import("builtin")` module corresponding to `opts` is available in
   2304 /// `builtin_modules`, and that its file is populated. Also ensures the file on disk is
   2305 /// up-to-date, setting a misc failure if updating it fails.
   2306 /// Asserts that the imported `builtin.zig` has no ZIR errors, and that it has only one
   2307 /// import, which is 'std'.
   2308 pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!void {
   2309     const zcu = pt.zcu;
   2310     const comp = zcu.comp;
   2311     const gpa = comp.gpa;
   2312     const io = comp.io;
   2313 
   2314     const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash());
   2315     if (gop.found_existing) return; // the `File` is up-to-date
   2316     errdefer _ = zcu.builtin_modules.pop();
   2317 
   2318     const mod: *Module = try .createBuiltin(comp.arena, opts, comp.dirs);
   2319     assert(std.mem.eql(u8, &mod.getBuiltinOptions(comp.config).hash(), gop.key_ptr)); // builtin is its own builtin
   2320 
   2321     const path = try mod.root.join(gpa, comp.dirs, "builtin.zig");
   2322     errdefer path.deinit(gpa);
   2323 
   2324     const file_gop = try zcu.import_table.getOrPutAdapted(gpa, path, Zcu.ImportTableAdapter{ .zcu = zcu });
   2325     // `Compilation.Path.isIllegalZigImport` checks guard file creation, so
   2326     // there isn't an `import_table` entry for this path yet.
   2327     assert(!file_gop.found_existing);
   2328     errdefer _ = zcu.import_table.pop();
   2329 
   2330     try zcu.module_roots.ensureUnusedCapacity(gpa, 1);
   2331 
   2332     const file = try gpa.create(Zcu.File);
   2333     errdefer gpa.destroy(file);
   2334 
   2335     file.* = .{
   2336         .status = .never_loaded,
   2337         .stat = undefined,
   2338         .path = path,
   2339         .is_builtin = true,
   2340         .source = null,
   2341         .tree = null,
   2342         .zir = null,
   2343         .zoir = null,
   2344         .mod = mod,
   2345         .sub_file_path = "builtin.zig",
   2346         .module_changed = false,
   2347         .prev_zir = null,
   2348         .zoir_invalidated = false,
   2349     };
   2350 
   2351     const file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{
   2352         .bin_digest = path.digest(),
   2353         .file = file,
   2354         .root_type = .none,
   2355     });
   2356 
   2357     gop.value_ptr.* = mod;
   2358     file_gop.key_ptr.* = file_index;
   2359     zcu.module_roots.putAssumeCapacityNoClobber(mod, file_index.toOptional());
   2360     try opts.populateFile(gpa, file);
   2361 
   2362     assert(file.status == .success);
   2363     assert(!file.zir.?.hasCompileErrors());
   2364     {
   2365         // Check that it has only one import, which is 'std'.
   2366         const imports_idx = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
   2367         assert(imports_idx != 0); // there is an import
   2368         const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_idx);
   2369         assert(extra.data.imports_len == 1); // there is exactly one import
   2370         const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra.end);
   2371         const import_path = file.zir.?.nullTerminatedString(item.data.name);
   2372         assert(mem.eql(u8, import_path, "std")); // the single import is of 'std'
   2373     }
   2374 
   2375     Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure(
   2376         .write_builtin_zig,
   2377         "unable to write '{f}': {s}",
   2378         .{ file.path.fmt(comp), @errorName(err) },
   2379     );
   2380 }
   2381 
   2382 pub fn embedFile(
   2383     pt: Zcu.PerThread,
   2384     cur_file: *Zcu.File,
   2385     import_string: []const u8,
   2386 ) error{
   2387     OutOfMemory,
   2388     Canceled,
   2389     ImportOutsideModulePath,
   2390     CurrentWorkingDirectoryUnlinked,
   2391 }!Zcu.EmbedFile.Index {
   2392     const zcu = pt.zcu;
   2393     const gpa = zcu.gpa;
   2394 
   2395     const opt_mod: ?*Module = m: {
   2396         if (mem.eql(u8, import_string, "std")) break :m zcu.std_mod;
   2397         if (mem.eql(u8, import_string, "root")) break :m zcu.root_mod;
   2398         if (mem.eql(u8, import_string, "builtin")) {
   2399             const opts = cur_file.mod.?.getBuiltinOptions(zcu.comp.config);
   2400             break :m zcu.builtin_modules.get(opts.hash()).?;
   2401         }
   2402         break :m cur_file.mod.?.deps.get(import_string);
   2403     };
   2404     if (opt_mod) |mod| {
   2405         const path = try mod.root.join(gpa, zcu.comp.dirs, mod.root_src_path);
   2406         errdefer path.deinit(gpa);
   2407 
   2408         const gop = try zcu.embed_table.getOrPutAdapted(gpa, path, Zcu.EmbedTableAdapter{});
   2409         if (gop.found_existing) {
   2410             path.deinit(gpa); // we're not using this key
   2411             return @enumFromInt(gop.index);
   2412         }
   2413         errdefer _ = zcu.embed_table.pop();
   2414         gop.key_ptr.* = try pt.newEmbedFile(path);
   2415         return @enumFromInt(gop.index);
   2416     }
   2417 
   2418     const embed_file: *Zcu.EmbedFile, const embed_file_idx: Zcu.EmbedFile.Index = ef: {
   2419         const path = try cur_file.path.upJoin(gpa, zcu.comp.dirs, import_string);
   2420         errdefer path.deinit(gpa);
   2421         const gop = try zcu.embed_table.getOrPutAdapted(gpa, path, Zcu.EmbedTableAdapter{});
   2422         if (gop.found_existing) {
   2423             path.deinit(gpa); // we're not using this key
   2424             break :ef .{ gop.key_ptr.*, @enumFromInt(gop.index) };
   2425         } else {
   2426             errdefer _ = zcu.embed_table.pop();
   2427             gop.key_ptr.* = try pt.newEmbedFile(path);
   2428             break :ef .{ gop.key_ptr.*, @enumFromInt(gop.index) };
   2429         }
   2430     };
   2431 
   2432     switch (embed_file.path.isNested(cur_file.mod.?.root)) {
   2433         .yes => {},
   2434         .different_roots, .no => return error.ImportOutsideModulePath,
   2435     }
   2436 
   2437     return embed_file_idx;
   2438 }
   2439 
   2440 pub fn updateEmbedFile(
   2441     pt: Zcu.PerThread,
   2442     ef: *Zcu.EmbedFile,
   2443     /// If not `null`, the interned file data is stored here, if it was loaded.
   2444     /// `newEmbedFile` uses this to add the file to the `whole` cache manifest.
   2445     ip_str_out: ?*?InternPool.String,
   2446 ) Allocator.Error!void {
   2447     pt.updateEmbedFileInner(ef, ip_str_out) catch |err| switch (err) {
   2448         error.OutOfMemory => |e| return e,
   2449         else => |e| {
   2450             ef.val = .none;
   2451             ef.err = e;
   2452             ef.stat = undefined;
   2453         },
   2454     };
   2455 }
   2456 
   2457 fn updateEmbedFileInner(
   2458     pt: Zcu.PerThread,
   2459     ef: *Zcu.EmbedFile,
   2460     ip_str_out: ?*?InternPool.String,
   2461 ) !void {
   2462     const tid = pt.tid;
   2463     const zcu = pt.zcu;
   2464     const gpa = zcu.gpa;
   2465     const io = zcu.comp.io;
   2466     const ip = &zcu.intern_pool;
   2467 
   2468     var file = f: {
   2469         const dir, const sub_path = ef.path.openInfo(zcu.comp.dirs);
   2470         break :f try dir.openFile(io, sub_path, .{});
   2471     };
   2472     defer file.close(io);
   2473 
   2474     const stat: Cache.File.Stat = .fromFs(try file.stat(io));
   2475 
   2476     if (ef.val != .none) {
   2477         const old_stat = ef.stat;
   2478         const unchanged_metadata =
   2479             stat.size == old_stat.size and
   2480             stat.mtime.nanoseconds == old_stat.mtime.nanoseconds and
   2481             stat.inode == old_stat.inode;
   2482         if (unchanged_metadata) return;
   2483     }
   2484 
   2485     const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
   2486     const size_plus_one = std.math.add(usize, size, 1) catch return error.FileTooBig;
   2487 
   2488     // The loaded bytes of the file, including a sentinel 0 byte.
   2489     const ip_str: InternPool.String = str: {
   2490         const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io);
   2491         const old_len = string_bytes.mutate.len;
   2492         errdefer string_bytes.shrinkRetainingCapacity(old_len);
   2493         const bytes = (try string_bytes.addManyAsSlice(size_plus_one))[0];
   2494         var fr = file.reader(io, &.{});
   2495         fr.size = stat.size;
   2496         fr.interface.readSliceAll(bytes[0..size]) catch |err| switch (err) {
   2497             error.ReadFailed => return fr.err.?,
   2498             error.EndOfStream => return error.UnexpectedEof,
   2499         };
   2500         bytes[size] = 0;
   2501         break :str try ip.getOrPutTrailingString(gpa, io, tid, @intCast(bytes.len), .maybe_embedded_nulls);
   2502     };
   2503     if (ip_str_out) |p| p.* = ip_str;
   2504 
   2505     const array_ty = try pt.arrayType(.{
   2506         .len = size,
   2507         .sentinel = .zero_u8,
   2508         .child = .u8_type,
   2509     });
   2510     const ptr_ty = try pt.singleConstPtrType(array_ty);
   2511 
   2512     const array_val = try pt.intern(.{ .aggregate = .{
   2513         .ty = array_ty.toIntern(),
   2514         .storage = .{ .bytes = ip_str },
   2515     } });
   2516     const ptr_val = try pt.intern(.{ .ptr = .{
   2517         .ty = ptr_ty.toIntern(),
   2518         .base_addr = .{ .uav = .{
   2519             .val = array_val,
   2520             .orig_ty = ptr_ty.toIntern(),
   2521         } },
   2522         .byte_offset = 0,
   2523     } });
   2524 
   2525     ef.val = ptr_val;
   2526     ef.err = null;
   2527     ef.stat = stat;
   2528 }
   2529 
   2530 /// Assumes that `path` is allocated into `gpa`. Takes ownership of `path` on success.
   2531 fn newEmbedFile(
   2532     pt: Zcu.PerThread,
   2533     path: Compilation.Path,
   2534 ) !*Zcu.EmbedFile {
   2535     const zcu = pt.zcu;
   2536     const comp = zcu.comp;
   2537     const io = comp.io;
   2538     const gpa = comp.gpa;
   2539     const ip = &zcu.intern_pool;
   2540 
   2541     const new_file = try gpa.create(Zcu.EmbedFile);
   2542     errdefer gpa.destroy(new_file);
   2543 
   2544     new_file.* = .{
   2545         .path = path,
   2546         .val = .none,
   2547         .err = null,
   2548         .stat = undefined,
   2549     };
   2550 
   2551     var opt_ip_str: ?InternPool.String = null;
   2552     try pt.updateEmbedFile(new_file, &opt_ip_str);
   2553 
   2554     try comp.appendFileSystemInput(path);
   2555 
   2556     // Add the file contents to the `whole` cache manifest if necessary.
   2557     cache: {
   2558         const whole = switch (zcu.comp.cache_use) {
   2559             .whole => |whole| whole,
   2560             .incremental, .none => break :cache,
   2561         };
   2562         const man = whole.cache_manifest orelse break :cache;
   2563         const ip_str = opt_ip_str orelse break :cache; // this will be a compile error
   2564 
   2565         const array_len = Value.fromInterned(new_file.val).typeOf(zcu).childType(zcu).arrayLen(zcu);
   2566         const contents = ip_str.toSlice(array_len, ip);
   2567 
   2568         const path_str = try path.toAbsolute(comp.dirs, gpa);
   2569         defer gpa.free(path_str);
   2570 
   2571         try whole.cache_manifest_mutex.lock(io);
   2572         defer whole.cache_manifest_mutex.unlock(io);
   2573 
   2574         man.addFilePostContents(path_str, contents, new_file.stat) catch |err| switch (err) {
   2575             error.Unexpected => unreachable,
   2576             else => |e| return e,
   2577         };
   2578     }
   2579 
   2580     return new_file;
   2581 }
   2582 
   2583 pub fn scanNamespace(
   2584     pt: Zcu.PerThread,
   2585     namespace_index: Zcu.Namespace.Index,
   2586     decls: []const Zir.Inst.Index,
   2587 ) Allocator.Error!void {
   2588     const tracy = trace(@src());
   2589     defer tracy.end();
   2590 
   2591     const zcu = pt.zcu;
   2592     const ip = &zcu.intern_pool;
   2593     const gpa = zcu.gpa;
   2594     const namespace = zcu.namespacePtr(namespace_index);
   2595 
   2596     const tracked_unit = zcu.trackUnitSema(
   2597         Type.fromInterned(namespace.owner_type).containerTypeName(ip).toSlice(ip),
   2598         null,
   2599     );
   2600     defer tracked_unit.end(zcu);
   2601 
   2602     // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather
   2603     // than their name. We'll build an efficient mapping now, then discard the current `decls`.
   2604     // We map to the `AnalUnit`, since not every declaration has a `Nav`.
   2605     var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit) = .empty;
   2606     defer existing_by_inst.deinit(gpa);
   2607 
   2608     try existing_by_inst.ensureTotalCapacity(gpa, @intCast(
   2609         namespace.pub_decls.count() + namespace.priv_decls.count() +
   2610             namespace.comptime_decls.items.len +
   2611             namespace.test_decls.items.len,
   2612     ));
   2613 
   2614     for (namespace.pub_decls.keys()) |nav| {
   2615         const zir_index = ip.getNav(nav).analysis.?.zir_index;
   2616         existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
   2617     }
   2618     for (namespace.priv_decls.keys()) |nav| {
   2619         const zir_index = ip.getNav(nav).analysis.?.zir_index;
   2620         existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
   2621     }
   2622     for (namespace.comptime_decls.items) |cu| {
   2623         const zir_index = ip.getComptimeUnit(cu).zir_index;
   2624         existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu }));
   2625     }
   2626     for (namespace.test_decls.items) |nav| {
   2627         const zir_index = ip.getNav(nav).analysis.?.zir_index;
   2628         existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
   2629         // This test will be re-added to `test_functions` later on if it's still alive. Remove it for now.
   2630         _ = zcu.test_functions.swapRemove(nav);
   2631     }
   2632 
   2633     var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
   2634     defer seen_decls.deinit(gpa);
   2635 
   2636     namespace.pub_decls.clearRetainingCapacity();
   2637     namespace.priv_decls.clearRetainingCapacity();
   2638     namespace.comptime_decls.clearRetainingCapacity();
   2639     namespace.test_decls.clearRetainingCapacity();
   2640 
   2641     var scan_decl_iter: ScanDeclIter = .{
   2642         .pt = pt,
   2643         .namespace_index = namespace_index,
   2644         .seen_decls = &seen_decls,
   2645         .existing_by_inst = &existing_by_inst,
   2646         .pass = .named,
   2647     };
   2648     for (decls) |decl_inst| {
   2649         try scan_decl_iter.scanDecl(decl_inst);
   2650     }
   2651     scan_decl_iter.pass = .unnamed;
   2652     for (decls) |decl_inst| {
   2653         try scan_decl_iter.scanDecl(decl_inst);
   2654     }
   2655 }
   2656 
   2657 const ScanDeclIter = struct {
   2658     pt: Zcu.PerThread,
   2659     namespace_index: Zcu.Namespace.Index,
   2660     seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
   2661     existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit),
   2662     /// Decl scanning is run in two passes, so that we can detect when a generated
   2663     /// name would clash with an explicit name and use a different one.
   2664     pass: enum { named, unnamed },
   2665     unnamed_test_index: usize = 0,
   2666 
   2667     fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {
   2668         const pt = iter.pt;
   2669         const ip = &pt.zcu.intern_pool;
   2670         const comp = pt.zcu.comp;
   2671         const gpa = comp.gpa;
   2672         const io = comp.io;
   2673         var name = try ip.getOrPutStringFmt(gpa, io, pt.tid, fmt, args, .no_embedded_nulls);
   2674         var gop = try iter.seen_decls.getOrPut(gpa, name);
   2675         var next_suffix: u32 = 0;
   2676         while (gop.found_existing) {
   2677             name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls);
   2678             gop = try iter.seen_decls.getOrPut(gpa, name);
   2679             next_suffix += 1;
   2680         }
   2681         return name;
   2682     }
   2683 
   2684     fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void {
   2685         const tracy = trace(@src());
   2686         defer tracy.end();
   2687 
   2688         const pt = iter.pt;
   2689         const zcu = pt.zcu;
   2690         const comp = zcu.comp;
   2691         const namespace_index = iter.namespace_index;
   2692         const namespace = zcu.namespacePtr(namespace_index);
   2693         const gpa = comp.gpa;
   2694         const io = comp.io;
   2695         const file = namespace.fileScope(zcu);
   2696         const zir = file.zir.?;
   2697         const ip = &zcu.intern_pool;
   2698 
   2699         const decl = zir.getDeclaration(decl_inst);
   2700 
   2701         const maybe_name: InternPool.OptionalNullTerminatedString = switch (decl.kind) {
   2702             .@"comptime" => name: {
   2703                 if (iter.pass != .unnamed) return;
   2704                 break :name .none;
   2705             },
   2706             .unnamed_test => name: {
   2707                 if (iter.pass != .unnamed) return;
   2708                 const i = iter.unnamed_test_index;
   2709                 iter.unnamed_test_index += 1;
   2710                 break :name (try iter.avoidNameConflict("test_{d}", .{i})).toOptional();
   2711             },
   2712             .@"test", .decltest => |kind| name: {
   2713                 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
   2714                 if (iter.pass != .unnamed) return;
   2715                 const prefix = @tagName(kind);
   2716                 break :name (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(decl.name) })).toOptional();
   2717             },
   2718             .@"const", .@"var" => name: {
   2719                 if (iter.pass != .named) return;
   2720                 const name = try ip.getOrPutString(
   2721                     gpa,
   2722                     io,
   2723                     pt.tid,
   2724                     zir.nullTerminatedString(decl.name),
   2725                     .no_embedded_nulls,
   2726                 );
   2727                 try iter.seen_decls.putNoClobber(gpa, name, {});
   2728                 break :name name.toOptional();
   2729             },
   2730         };
   2731 
   2732         const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{
   2733             .file = namespace.file_scope,
   2734             .inst = decl_inst,
   2735         });
   2736 
   2737         const existing_unit = iter.existing_by_inst.get(tracked_inst);
   2738 
   2739         const unit, const want_analysis = switch (decl.kind) {
   2740             .@"comptime" => unit: {
   2741                 const cu = if (existing_unit) |eu|
   2742                     eu.unwrap().@"comptime"
   2743                 else
   2744                     try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index);
   2745 
   2746                 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
   2747 
   2748                 try namespace.comptime_decls.append(gpa, cu);
   2749 
   2750                 if (existing_unit == null) {
   2751                     // For a `comptime` declaration, whether to analyze is based solely on whether the unit
   2752                     // is outdated. So, add this fresh one to `outdated` and `outdated_ready`.
   2753                     try zcu.outdated.ensureUnusedCapacity(gpa, 1);
   2754                     try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
   2755                     zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
   2756                     zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
   2757                 }
   2758 
   2759                 break :unit .{ unit, true };
   2760             },
   2761             else => unit: {
   2762                 const name = maybe_name.unwrap().?;
   2763                 const fqn = try namespace.internFullyQualifiedName(ip, gpa, io, pt.tid, name);
   2764                 const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: {
   2765                     const nav = try ip.createDeclNav(gpa, io, pt.tid, name, fqn, tracked_inst, namespace_index);
   2766                     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
   2767                     break :nav nav;
   2768                 };
   2769 
   2770                 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
   2771 
   2772                 assert(ip.getNav(nav).name == name);
   2773                 assert(ip.getNav(nav).fqn == fqn);
   2774 
   2775                 const want_analysis = switch (decl.kind) {
   2776                     .@"comptime" => unreachable,
   2777                     .unnamed_test, .@"test", .decltest => a: {
   2778                         const is_named = decl.kind != .unnamed_test;
   2779                         try namespace.test_decls.append(gpa, nav);
   2780                         // TODO: incremental compilation!
   2781                         // * remove from `test_functions` if no longer matching filter
   2782                         // * add to `test_functions` if newly passing filter
   2783                         // This logic is unaware of incremental: we'll end up with duplicates.
   2784                         // Perhaps we should add all test indiscriminately and filter at the end of the update.
   2785                         if (!comp.config.is_test) break :a false;
   2786                         if (file.mod != zcu.main_mod) break :a false;
   2787                         if (is_named and comp.test_filters.len > 0) {
   2788                             const fqn_slice = fqn.toSlice(ip);
   2789                             for (comp.test_filters) |test_filter| {
   2790                                 if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
   2791                             } else break :a false;
   2792                         }
   2793                         try zcu.test_functions.put(gpa, nav, {});
   2794                         break :a true;
   2795                     },
   2796                     .@"const", .@"var" => a: {
   2797                         if (decl.is_pub) {
   2798                             try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
   2799                         } else {
   2800                             try namespace.priv_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
   2801                         }
   2802                         break :a false;
   2803                     },
   2804                 };
   2805                 break :unit .{ unit, want_analysis };
   2806             },
   2807         };
   2808 
   2809         if (existing_unit == null and (want_analysis or decl.linkage == .@"export")) {
   2810             log.debug(
   2811                 "scanDecl queue analyze_comptime_unit file='{s}' unit={f}",
   2812                 .{ namespace.fileScope(zcu).sub_file_path, zcu.fmtAnalUnit(unit) },
   2813             );
   2814             try comp.queueJob(.{ .analyze_comptime_unit = unit });
   2815         }
   2816     }
   2817 };
   2818 
   2819 fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
   2820     const tracy = trace(@src());
   2821     defer tracy.end();
   2822 
   2823     const zcu = pt.zcu;
   2824     const comp = zcu.comp;
   2825     const gpa = comp.gpa;
   2826     const io = comp.io;
   2827     const ip = &zcu.intern_pool;
   2828 
   2829     const anal_unit = AnalUnit.wrap(.{ .func = func_index });
   2830     const func = zcu.funcInfo(func_index);
   2831     const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
   2832     const file = zcu.fileByIndex(inst_info.file);
   2833     const zir = file.zir.?;
   2834 
   2835     try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
   2836     errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
   2837 
   2838     func.setAnalyzed(ip, io);
   2839     if (func.analysisUnordered(ip).inferred_error_set) {
   2840         func.setResolvedErrorSet(ip, io, .none);
   2841     }
   2842 
   2843     if (zcu.comp.time_report) |*tr| {
   2844         if (func.generic_owner != .none) {
   2845             tr.stats.n_generic_instances += 1;
   2846         }
   2847     }
   2848 
   2849     // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from.
   2850     const decl_nav = ip.getNav(if (func.generic_owner == .none)
   2851         func.owner_nav
   2852     else
   2853         zcu.funcInfo(func.generic_owner).owner_nav);
   2854 
   2855     const func_nav = ip.getNav(func.owner_nav);
   2856 
   2857     zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
   2858 
   2859     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
   2860     defer analysis_arena.deinit();
   2861 
   2862     var comptime_err_ret_trace = std.array_list.Managed(Zcu.LazySrcLoc).init(gpa);
   2863     defer comptime_err_ret_trace.deinit();
   2864 
   2865     // In the case of a generic function instance, this is the type of the
   2866     // instance, which has comptime parameters elided. In other words, it is
   2867     // the runtime-known parameters only, not to be confused with the
   2868     // generic_owner function type, which potentially has more parameters,
   2869     // including comptime parameters.
   2870     const fn_ty = Type.fromInterned(func.ty);
   2871     const fn_ty_info = zcu.typeToFunc(fn_ty).?;
   2872 
   2873     var sema: Sema = .{
   2874         .pt = pt,
   2875         .gpa = gpa,
   2876         .arena = analysis_arena.allocator(),
   2877         .code = zir,
   2878         .owner = anal_unit,
   2879         .func_index = func_index,
   2880         .func_is_naked = fn_ty_info.cc == .naked,
   2881         .fn_ret_ty = Type.fromInterned(fn_ty_info.return_type),
   2882         .fn_ret_ty_ies = null,
   2883         .branch_quota = @max(func.branchQuotaUnordered(ip), Sema.default_branch_quota),
   2884         .comptime_err_ret_trace = &comptime_err_ret_trace,
   2885     };
   2886     defer sema.deinit();
   2887 
   2888     // Every runtime function has a dependency on the source of the Decl it originates from.
   2889     // It also depends on the value of its owner Decl.
   2890     try sema.declareDependency(.{ .src_hash = decl_nav.analysis.?.zir_index });
   2891     try sema.declareDependency(.{ .nav_val = func.owner_nav });
   2892 
   2893     if (func.analysisUnordered(ip).inferred_error_set) {
   2894         const ies = try analysis_arena.allocator().create(Sema.InferredErrorSet);
   2895         ies.* = .{ .func = func_index };
   2896         sema.fn_ret_ty_ies = ies;
   2897     }
   2898 
   2899     // reset in case calls to errorable functions are removed.
   2900     ip.funcSetHasErrorTrace(io, func_index, fn_ty_info.cc == .auto);
   2901 
   2902     // First few indexes of extra are reserved and set at the end.
   2903     const reserved_count = @typeInfo(Air.ExtraIndex).@"enum".fields.len;
   2904     try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
   2905     sema.air_extra.items.len += reserved_count;
   2906 
   2907     var inner_block: Sema.Block = .{
   2908         .parent = null,
   2909         .sema = &sema,
   2910         .namespace = decl_nav.analysis.?.namespace,
   2911         .instructions = .{},
   2912         .inlining = null,
   2913         .comptime_reason = null,
   2914         .src_base_inst = decl_nav.analysis.?.zir_index,
   2915         .type_name_ctx = func_nav.fqn,
   2916     };
   2917     defer inner_block.instructions.deinit(gpa);
   2918 
   2919     const fn_info = sema.code.getFnInfo(func.zirBodyInstUnordered(ip).resolve(ip) orelse return error.AnalysisFail);
   2920 
   2921     // Here we are performing "runtime semantic analysis" for a function body, which means
   2922     // we must map the parameter ZIR instructions to `arg` AIR instructions.
   2923     // AIR requires the `arg` parameters to be the first N instructions.
   2924     // This could be a generic function instantiation, however, in which case we need to
   2925     // map the comptime parameters to constant values and only emit arg AIR instructions
   2926     // for the runtime ones.
   2927     const runtime_params_len = fn_ty_info.param_types.len;
   2928     try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len);
   2929     try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len);
   2930     try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
   2931 
   2932     // In the case of a generic function instance, pre-populate all the comptime args.
   2933     if (func.comptime_args.len != 0) {
   2934         for (
   2935             fn_info.param_body[0..func.comptime_args.len],
   2936             func.comptime_args.get(ip),
   2937         ) |inst, comptime_arg| {
   2938             if (comptime_arg == .none) continue;
   2939             sema.inst_map.putAssumeCapacityNoClobber(inst, Air.internedToRef(comptime_arg));
   2940         }
   2941     }
   2942 
   2943     const src_params_len = if (func.comptime_args.len != 0)
   2944         func.comptime_args.len
   2945     else
   2946         runtime_params_len;
   2947 
   2948     var runtime_param_index: usize = 0;
   2949     for (fn_info.param_body[0..src_params_len], 0..) |inst, zir_param_index| {
   2950         const gop = sema.inst_map.getOrPutAssumeCapacity(inst);
   2951         if (gop.found_existing) continue; // provided above by comptime arg
   2952 
   2953         const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
   2954         runtime_param_index += 1;
   2955 
   2956         const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) {
   2957             error.ComptimeReturn => unreachable,
   2958             error.ComptimeBreak => unreachable,
   2959             else => |e| return e,
   2960         };
   2961         if (opt_opv) |opv| {
   2962             gop.value_ptr.* = Air.internedToRef(opv.toIntern());
   2963             continue;
   2964         }
   2965         const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   2966         gop.value_ptr.* = arg_index.toRef();
   2967         inner_block.instructions.appendAssumeCapacity(arg_index);
   2968         sema.air_instructions.appendAssumeCapacity(.{
   2969             .tag = .arg,
   2970             .data = .{ .arg = .{
   2971                 .ty = Air.internedToRef(param_ty),
   2972                 .zir_param_index = @intCast(zir_param_index),
   2973             } },
   2974         });
   2975     }
   2976 
   2977     const last_arg_index = inner_block.instructions.items.len;
   2978 
   2979     // Save the error trace as our first action in the function.
   2980     // If this is unnecessary after all, Liveness will clean it up for us.
   2981     const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&inner_block);
   2982     sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
   2983     inner_block.error_return_trace_index = error_return_trace_index;
   2984 
   2985     sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) {
   2986         error.ComptimeReturn => unreachable,
   2987         else => |e| return e,
   2988     };
   2989 
   2990     for (sema.unresolved_inferred_allocs.keys()) |ptr_inst| {
   2991         // The lack of a resolve_inferred_alloc means that this instruction
   2992         // is unused so it just has to be a no-op.
   2993         sema.air_instructions.set(@intFromEnum(ptr_inst), .{
   2994             .tag = .alloc,
   2995             .data = .{ .ty = .ptr_const_comptime_int },
   2996         });
   2997     }
   2998 
   2999     func.setBranchHint(ip, io, sema.branch_hint orelse .none);
   3000 
   3001     if (zcu.comp.config.any_error_tracing and func.analysisUnordered(ip).has_error_trace and fn_ty_info.cc != .auto) {
   3002         // We're using an error trace, but didn't start out with one from the caller.
   3003         // We'll have to create it at the start of the function.
   3004         sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) {
   3005             error.ComptimeReturn => unreachable,
   3006             error.ComptimeBreak => unreachable,
   3007             else => |e| return e,
   3008         };
   3009     }
   3010 
   3011     // Copy the block into place and mark that as the main block.
   3012     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   3013         inner_block.instructions.items.len);
   3014     const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
   3015         .body_len = @intCast(inner_block.instructions.items.len),
   3016     });
   3017     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(inner_block.instructions.items));
   3018     sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;
   3019 
   3020     // Resolving inferred error sets is done *before* setting the function
   3021     // state to success, so that "unable to resolve inferred error set" errors
   3022     // can be emitted here.
   3023     if (sema.fn_ret_ty_ies) |ies| {
   3024         sema.resolveInferredErrorSetPtr(&inner_block, .{
   3025             .base_node_inst = inner_block.src_base_inst,
   3026             .offset = Zcu.LazySrcLoc.Offset.nodeOffset(.zero),
   3027         }, ies) catch |err| switch (err) {
   3028             error.ComptimeReturn => unreachable,
   3029             error.ComptimeBreak => unreachable,
   3030             else => |e| return e,
   3031         };
   3032         assert(ies.resolved != .none);
   3033         func.setResolvedErrorSet(ip, io, ies.resolved);
   3034     }
   3035 
   3036     assert(zcu.analysis_in_progress.swapRemove(anal_unit));
   3037 
   3038     // Finally we must resolve the return type and parameter types so that backends
   3039     // have full access to type information.
   3040     // Crucially, this happens *after* we set the function state to success above,
   3041     // so that dependencies on the function body will now be satisfied rather than
   3042     // result in circular dependency errors.
   3043     // TODO: this can go away once we fix backends having to resolve `StackTrace`.
   3044     // The codegen timing guarantees that the parameter types will be populated.
   3045     sema.resolveFnTypes(fn_ty, inner_block.nodeOffset(.zero)) catch |err| switch (err) {
   3046         error.ComptimeReturn => unreachable,
   3047         error.ComptimeBreak => unreachable,
   3048         else => |e| return e,
   3049     };
   3050 
   3051     try sema.flushExports();
   3052 
   3053     defer {
   3054         sema.air_instructions = .empty;
   3055         sema.air_extra = .empty;
   3056     }
   3057     return .{
   3058         .instructions = sema.air_instructions.slice(),
   3059         .extra = sema.air_extra,
   3060     };
   3061 }
   3062 
   3063 pub fn createNamespace(pt: Zcu.PerThread, initialization: Zcu.Namespace) !Zcu.Namespace.Index {
   3064     const comp = pt.zcu.comp;
   3065     return pt.zcu.intern_pool.createNamespace(comp.gpa, comp.io, pt.tid, initialization);
   3066 }
   3067 
   3068 pub fn destroyNamespace(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) void {
   3069     return pt.zcu.intern_pool.destroyNamespace(pt.tid, namespace_index);
   3070 }
   3071 
   3072 pub fn getErrorValue(
   3073     pt: Zcu.PerThread,
   3074     name: InternPool.NullTerminatedString,
   3075 ) Allocator.Error!Zcu.ErrorInt {
   3076     const comp = pt.zcu.comp;
   3077     return pt.zcu.intern_pool.getErrorValue(comp.gpa, comp.io, pt.tid, name);
   3078 }
   3079 
   3080 pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Zcu.ErrorInt {
   3081     const comp = pt.zcu.comp;
   3082     const gpa = comp.gpa;
   3083     const io = comp.io;
   3084     return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(gpa, io, name));
   3085 }
   3086 
   3087 /// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed.
   3088 /// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry.
   3089 fn lockAndClearFileCompileError(pt: Zcu.PerThread, file_index: Zcu.File.Index, file: *Zcu.File) void {
   3090     const maybe_has_error = switch (file.status) {
   3091         .never_loaded => false,
   3092         .retryable_failure => true,
   3093         .astgen_failure => true,
   3094         .success => switch (file.getMode()) {
   3095             .zig => has_error: {
   3096                 const zir = file.zir orelse break :has_error false;
   3097                 break :has_error zir.hasCompileErrors();
   3098             },
   3099             .zon => has_error: {
   3100                 const zoir = file.zoir orelse break :has_error false;
   3101                 break :has_error zoir.hasCompileErrors();
   3102             },
   3103         },
   3104     };
   3105 
   3106     // If runtime safety is on, let's quickly lock the mutex and check anyway.
   3107     if (!maybe_has_error and !std.debug.runtime_safety) {
   3108         return;
   3109     }
   3110 
   3111     const comp = pt.zcu.comp;
   3112     const io = comp.io;
   3113     comp.mutex.lockUncancelable(io);
   3114     defer comp.mutex.unlock(io);
   3115     if (pt.zcu.failed_files.fetchSwapRemove(file_index)) |kv| {
   3116         assert(maybe_has_error); // the runtime safety case above
   3117         if (kv.value) |msg| pt.zcu.gpa.free(msg); // delete previous error message
   3118     }
   3119 }
   3120 
   3121 /// Called from `Compilation.update`, after everything is done, just before
   3122 /// reporting compile errors. In this function we emit exported symbol collision
   3123 /// errors and communicate exported symbols to the linker backend.
   3124 pub fn processExports(pt: Zcu.PerThread) !void {
   3125     const zcu = pt.zcu;
   3126     const gpa = zcu.gpa;
   3127 
   3128     if (zcu.single_exports.count() == 0 and zcu.multi_exports.count() == 0) {
   3129         // We can avoid a call to `resolveReferences` in this case.
   3130         return;
   3131     }
   3132 
   3133     // First, construct a mapping of every exported value and Nav to the indices of all its different exports.
   3134     var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayList(Zcu.Export.Index)) = .empty;
   3135     var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayList(Zcu.Export.Index)) = .empty;
   3136     defer {
   3137         for (nav_exports.values()) |*exports| {
   3138             exports.deinit(gpa);
   3139         }
   3140         nav_exports.deinit(gpa);
   3141         for (uav_exports.values()) |*exports| {
   3142             exports.deinit(gpa);
   3143         }
   3144         uav_exports.deinit(gpa);
   3145     }
   3146 
   3147     // We note as a heuristic:
   3148     // * It is rare to export a value.
   3149     // * It is rare for one Nav to be exported multiple times.
   3150     // So, this ensureTotalCapacity serves as a reasonable (albeit very approximate) optimization.
   3151     try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count());
   3152 
   3153     const unit_references = try zcu.resolveReferences();
   3154 
   3155     for (zcu.single_exports.keys(), zcu.single_exports.values()) |exporter, export_idx| {
   3156         const exp = export_idx.ptr(zcu);
   3157         if (!unit_references.contains(exporter)) {
   3158             // This export might already have been sent to the linker on a previous update, in which case we need to delete it.
   3159             // The linker export API should be modified to eliminate this call. #23616
   3160             if (zcu.comp.bin_file) |lf| {
   3161                 if (zcu.llvm_object == null) {
   3162                     lf.deleteExport(exp.exported, exp.opts.name);
   3163                 }
   3164             }
   3165             continue;
   3166         }
   3167         const value_ptr, const found_existing = switch (exp.exported) {
   3168             .nav => |nav| gop: {
   3169                 const gop = try nav_exports.getOrPut(gpa, nav);
   3170                 break :gop .{ gop.value_ptr, gop.found_existing };
   3171             },
   3172             .uav => |uav| gop: {
   3173                 const gop = try uav_exports.getOrPut(gpa, uav);
   3174                 break :gop .{ gop.value_ptr, gop.found_existing };
   3175             },
   3176         };
   3177         if (!found_existing) value_ptr.* = .{};
   3178         try value_ptr.append(gpa, export_idx);
   3179     }
   3180 
   3181     for (zcu.multi_exports.keys(), zcu.multi_exports.values()) |exporter, info| {
   3182         const exports = zcu.all_exports.items[info.index..][0..info.len];
   3183         if (!unit_references.contains(exporter)) {
   3184             // This export might already have been sent to the linker on a previous update, in which case we need to delete it.
   3185             // The linker export API should be modified to eliminate this loop. #23616
   3186             if (zcu.comp.bin_file) |lf| {
   3187                 if (zcu.llvm_object == null) {
   3188                     for (exports) |exp| {
   3189                         lf.deleteExport(exp.exported, exp.opts.name);
   3190                     }
   3191                 }
   3192             }
   3193             continue;
   3194         }
   3195         for (exports, info.index..) |exp, export_idx| {
   3196             const value_ptr, const found_existing = switch (exp.exported) {
   3197                 .nav => |nav| gop: {
   3198                     const gop = try nav_exports.getOrPut(gpa, nav);
   3199                     break :gop .{ gop.value_ptr, gop.found_existing };
   3200                 },
   3201                 .uav => |uav| gop: {
   3202                     const gop = try uav_exports.getOrPut(gpa, uav);
   3203                     break :gop .{ gop.value_ptr, gop.found_existing };
   3204                 },
   3205             };
   3206             if (!found_existing) value_ptr.* = .{};
   3207             try value_ptr.append(gpa, @enumFromInt(export_idx));
   3208         }
   3209     }
   3210 
   3211     // If there are compile errors, we won't call `updateExports`. Not only would it be redundant
   3212     // work, but the linker may not have seen an exported `Nav` due to a compile error, so linker
   3213     // implementations would have to handle that case. This early return avoids that.
   3214     const skip_linker_work = zcu.comp.anyErrors();
   3215 
   3216     // Map symbol names to `Export` for name collision detection.
   3217     var symbol_exports: SymbolExports = .{};
   3218     defer symbol_exports.deinit(gpa);
   3219 
   3220     for (nav_exports.keys(), nav_exports.values()) |exported_nav, exports_list| {
   3221         const exported: Zcu.Exported = .{ .nav = exported_nav };
   3222         try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work);
   3223     }
   3224 
   3225     for (uav_exports.keys(), uav_exports.values()) |exported_uav, exports_list| {
   3226         const exported: Zcu.Exported = .{ .uav = exported_uav };
   3227         try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work);
   3228     }
   3229 }
   3230 
   3231 const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Zcu.Export.Index);
   3232 
   3233 fn processExportsInner(
   3234     pt: Zcu.PerThread,
   3235     symbol_exports: *SymbolExports,
   3236     exported: Zcu.Exported,
   3237     export_indices: []const Zcu.Export.Index,
   3238     skip_linker_work: bool,
   3239 ) error{OutOfMemory}!void {
   3240     const zcu = pt.zcu;
   3241     const gpa = zcu.gpa;
   3242     const ip = &zcu.intern_pool;
   3243 
   3244     for (export_indices) |export_idx| {
   3245         const new_export = export_idx.ptr(zcu);
   3246         const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
   3247         if (gop.found_existing) {
   3248             new_export.status = .failed_retryable;
   3249             try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
   3250             const msg = try Zcu.ErrorMsg.create(gpa, new_export.src, "exported symbol collision: {f}", .{
   3251                 new_export.opts.name.fmt(ip),
   3252             });
   3253             errdefer msg.destroy(gpa);
   3254             const other_export = gop.value_ptr.ptr(zcu);
   3255             try zcu.errNote(other_export.src, msg, "other symbol here", .{});
   3256             zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
   3257             new_export.status = .failed;
   3258         } else {
   3259             gop.value_ptr.* = export_idx;
   3260         }
   3261     }
   3262 
   3263     switch (exported) {
   3264         .nav => |nav_index| if (failed: {
   3265             const nav = ip.getNav(nav_index);
   3266             if (zcu.failed_codegen.contains(nav_index)) break :failed true;
   3267             if (nav.analysis != null) {
   3268                 const unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
   3269                 if (zcu.failed_analysis.contains(unit)) break :failed true;
   3270                 if (zcu.transitive_failed_analysis.contains(unit)) break :failed true;
   3271             }
   3272             const val = switch (nav.status) {
   3273                 .unresolved, .type_resolved => break :failed true,
   3274                 .fully_resolved => |r| Value.fromInterned(r.val),
   3275             };
   3276             // If the value is a function, we also need to check if that function succeeded analysis.
   3277             if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") {
   3278                 const func_unit = AnalUnit.wrap(.{ .func = val.toIntern() });
   3279                 if (zcu.failed_analysis.contains(func_unit)) break :failed true;
   3280                 if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true;
   3281             }
   3282             break :failed false;
   3283         }) {
   3284             // This `Nav` is failed, so was never sent to codegen. There should be a compile error.
   3285             assert(skip_linker_work);
   3286         },
   3287         .uav => {},
   3288     }
   3289 
   3290     if (skip_linker_work) return;
   3291 
   3292     if (zcu.llvm_object) |llvm_object| {
   3293         try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(pt, exported, export_indices));
   3294     } else if (zcu.comp.bin_file) |lf| {
   3295         try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices));
   3296     }
   3297 }
   3298 
   3299 pub fn populateTestFunctions(pt: Zcu.PerThread) Allocator.Error!void {
   3300     const zcu = pt.zcu;
   3301     const comp = zcu.comp;
   3302     const gpa = comp.gpa;
   3303     const io = comp.io;
   3304     const ip = &zcu.intern_pool;
   3305 
   3306     // Our job is to correctly set the value of the `test_functions` declaration if it has been
   3307     // analyzed and sent to codegen, It usually will have been, because the test runner will
   3308     // reference it, and `std.builtin` shouldn't have type errors. However, if it hasn't been
   3309     // analyzed, we will just terminate early, since clearly the test runner hasn't referenced
   3310     // `test_functions` so there's no point populating it. More to the the point, we potentially
   3311     // *can't* populate it without doing some type resolution, and... let's try to leave Sema in
   3312     // the past here.
   3313 
   3314     const builtin_mod = zcu.builtin_modules.get(zcu.root_mod.getBuiltinOptions(zcu.comp.config).hash()).?;
   3315     const builtin_file_index = zcu.module_roots.get(builtin_mod).?.unwrap().?;
   3316     const builtin_root_type = zcu.fileRootType(builtin_file_index);
   3317     if (builtin_root_type == .none) return; // `@import("builtin")` never analyzed
   3318     const builtin_namespace = Type.fromInterned(builtin_root_type).getNamespace(zcu).unwrap().?;
   3319     // We know that the namespace has a `test_functions`...
   3320     const nav_index = zcu.namespacePtr(builtin_namespace).pub_decls.getKeyAdapted(
   3321         try ip.getOrPutString(gpa, io, pt.tid, "test_functions", .no_embedded_nulls),
   3322         Zcu.Namespace.NameAdapter{ .zcu = zcu },
   3323     ).?;
   3324     // ...but it might not be populated, so let's check that!
   3325     if (zcu.failed_analysis.contains(.wrap(.{ .nav_val = nav_index })) or
   3326         zcu.transitive_failed_analysis.contains(.wrap(.{ .nav_val = nav_index })) or
   3327         ip.getNav(nav_index).status != .fully_resolved)
   3328     {
   3329         // The value of `builtin.test_functions` was either never referenced, or failed analysis.
   3330         // Either way, we don't need to do anything.
   3331         return;
   3332     }
   3333 
   3334     // Okay, `builtin.test_functions` is (potentially) referenced and valid. Our job now is to swap
   3335     // its placeholder `&.{}` value for the actual list of all test functions.
   3336 
   3337     const test_fns_val = zcu.navValue(nav_index);
   3338     const test_fn_ty = test_fns_val.typeOf(zcu).slicePtrFieldType(zcu).childType(zcu);
   3339 
   3340     const array_anon_decl: InternPool.Key.Ptr.BaseAddr.Uav = array: {
   3341         // Add zcu.test_functions to an array decl then make the test_functions
   3342         // decl reference it as a slice.
   3343         const test_fn_vals = try gpa.alloc(InternPool.Index, zcu.test_functions.count());
   3344         defer gpa.free(test_fn_vals);
   3345 
   3346         for (test_fn_vals, zcu.test_functions.keys()) |*test_fn_val, test_nav_index| {
   3347             const test_nav = ip.getNav(test_nav_index);
   3348 
   3349             {
   3350                 // The test declaration might have failed; if that's the case, just return, as we'll
   3351                 // be emitting a compile error anyway.
   3352                 const anal_unit: AnalUnit = .wrap(.{ .nav_val = test_nav_index });
   3353                 if (zcu.failed_analysis.contains(anal_unit) or
   3354                     zcu.transitive_failed_analysis.contains(anal_unit))
   3355                 {
   3356                     return;
   3357                 }
   3358             }
   3359 
   3360             const test_nav_name = test_nav.fqn;
   3361             const test_nav_name_len = test_nav_name.length(ip);
   3362             const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.Uav = n: {
   3363                 const test_name_ty = try pt.arrayType(.{
   3364                     .len = test_nav_name_len,
   3365                     .child = .u8_type,
   3366                 });
   3367                 const test_name_val = try pt.intern(.{ .aggregate = .{
   3368                     .ty = test_name_ty.toIntern(),
   3369                     .storage = .{ .bytes = test_nav_name.toString() },
   3370                 } });
   3371                 break :n .{
   3372                     .orig_ty = (try pt.singleConstPtrType(test_name_ty)).toIntern(),
   3373                     .val = test_name_val,
   3374                 };
   3375             };
   3376 
   3377             const test_fn_fields = .{
   3378                 // name
   3379                 try pt.intern(.{ .slice = .{
   3380                     .ty = .slice_const_u8_type,
   3381                     .ptr = try pt.intern(.{ .ptr = .{
   3382                         .ty = .manyptr_const_u8_type,
   3383                         .base_addr = .{ .uav = test_name_anon_decl },
   3384                         .byte_offset = 0,
   3385                     } }),
   3386                     .len = try pt.intern(.{ .int = .{
   3387                         .ty = .usize_type,
   3388                         .storage = .{ .u64 = test_nav_name_len },
   3389                     } }),
   3390                 } }),
   3391                 // func
   3392                 try pt.intern(.{ .ptr = .{
   3393                     .ty = (try pt.navPtrType(test_nav_index)).toIntern(),
   3394                     .base_addr = .{ .nav = test_nav_index },
   3395                     .byte_offset = 0,
   3396                 } }),
   3397             };
   3398             test_fn_val.* = (try pt.aggregateValue(test_fn_ty, &test_fn_fields)).toIntern();
   3399         }
   3400 
   3401         const array_ty = try pt.arrayType(.{
   3402             .len = test_fn_vals.len,
   3403             .child = test_fn_ty.toIntern(),
   3404             .sentinel = .none,
   3405         });
   3406         break :array .{
   3407             .orig_ty = (try pt.singleConstPtrType(array_ty)).toIntern(),
   3408             .val = (try pt.aggregateValue(array_ty, test_fn_vals)).toIntern(),
   3409         };
   3410     };
   3411 
   3412     {
   3413         const new_ty = try pt.ptrType(.{
   3414             .child = test_fn_ty.toIntern(),
   3415             .flags = .{
   3416                 .is_const = true,
   3417                 .size = .slice,
   3418             },
   3419         });
   3420         const new_init = try pt.intern(.{ .slice = .{
   3421             .ty = new_ty.toIntern(),
   3422             .ptr = try pt.intern(.{ .ptr = .{
   3423                 .ty = new_ty.slicePtrFieldType(zcu).toIntern(),
   3424                 .base_addr = .{ .uav = array_anon_decl },
   3425                 .byte_offset = 0,
   3426             } }),
   3427             .len = (try pt.intValue(Type.usize, zcu.test_functions.count())).toIntern(),
   3428         } });
   3429         ip.mutateVarInit(io, test_fns_val.toIntern(), new_init);
   3430     }
   3431     // The linker thread is not running, so we actually need to dispatch this task directly.
   3432     @import("../link.zig").linkTestFunctionsNav(pt, nav_index);
   3433 }
   3434 
   3435 /// Stores an error in `pt.zcu.failed_files` for this file, and sets the file
   3436 /// status to `retryable_failure`.
   3437 pub fn reportRetryableFileError(
   3438     pt: Zcu.PerThread,
   3439     file_index: Zcu.File.Index,
   3440     comptime format: []const u8,
   3441     args: anytype,
   3442 ) error{OutOfMemory}!void {
   3443     const zcu = pt.zcu;
   3444     const comp = zcu.comp;
   3445     const io = comp.io;
   3446     const gpa = comp.gpa;
   3447 
   3448     const file = zcu.fileByIndex(file_index);
   3449 
   3450     file.status = .retryable_failure;
   3451 
   3452     const msg = try std.fmt.allocPrint(gpa, format, args);
   3453     errdefer gpa.free(msg);
   3454 
   3455     const old_msg: ?[]u8 = old_msg: {
   3456         comp.mutex.lockUncancelable(io);
   3457         defer comp.mutex.unlock(io);
   3458 
   3459         const gop = try zcu.failed_files.getOrPut(gpa, file_index);
   3460         const old: ?[]u8 = if (gop.found_existing) old: {
   3461             break :old gop.value_ptr.*;
   3462         } else null;
   3463         gop.value_ptr.* = msg;
   3464 
   3465         break :old_msg old;
   3466     };
   3467     if (old_msg) |m| gpa.free(m);
   3468 }
   3469 
   3470 /// Shortcut for calling `intern_pool.get`.
   3471 pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index {
   3472     const comp = pt.zcu.comp;
   3473     return pt.zcu.intern_pool.get(comp.gpa, comp.io, pt.tid, key);
   3474 }
   3475 
   3476 /// Essentially a shortcut for calling `intern_pool.getCoerced`.
   3477 /// However, this function also allows coercing `extern`s. The `InternPool` function can't do
   3478 /// this because it requires potentially pushing to the job queue.
   3479 pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value {
   3480     const ip = &pt.zcu.intern_pool;
   3481     const comp = pt.zcu.comp;
   3482     const gpa = comp.gpa;
   3483     const io = comp.io;
   3484     switch (ip.indexToKey(val.toIntern())) {
   3485         .@"extern" => |e| {
   3486             const coerced = try pt.getExtern(.{
   3487                 .name = e.name,
   3488                 .ty = new_ty.toIntern(),
   3489                 .lib_name = e.lib_name,
   3490                 .is_const = e.is_const,
   3491                 .is_threadlocal = e.is_threadlocal,
   3492                 .linkage = e.linkage,
   3493                 .visibility = e.visibility,
   3494                 .is_dll_import = e.is_dll_import,
   3495                 .relocation = e.relocation,
   3496                 .alignment = e.alignment,
   3497                 .@"addrspace" = e.@"addrspace",
   3498                 .zir_index = e.zir_index,
   3499                 .owner_nav = undefined, // ignored by `getExtern`.
   3500                 .source = e.source,
   3501             });
   3502             return Value.fromInterned(coerced);
   3503         },
   3504         else => {},
   3505     }
   3506     return Value.fromInterned(try ip.getCoerced(gpa, io, pt.tid, val.toIntern(), new_ty.toIntern()));
   3507 }
   3508 
   3509 pub fn intType(pt: Zcu.PerThread, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type {
   3510     return Type.fromInterned(try pt.intern(.{ .int_type = .{
   3511         .signedness = signedness,
   3512         .bits = bits,
   3513     } }));
   3514 }
   3515 
   3516 pub fn errorIntType(pt: Zcu.PerThread) std.mem.Allocator.Error!Type {
   3517     return pt.intType(.unsigned, pt.zcu.errorSetBits());
   3518 }
   3519 
   3520 pub fn arrayType(pt: Zcu.PerThread, info: InternPool.Key.ArrayType) Allocator.Error!Type {
   3521     return Type.fromInterned(try pt.intern(.{ .array_type = info }));
   3522 }
   3523 
   3524 pub fn vectorType(pt: Zcu.PerThread, info: InternPool.Key.VectorType) Allocator.Error!Type {
   3525     return Type.fromInterned(try pt.intern(.{ .vector_type = info }));
   3526 }
   3527 
   3528 pub fn optionalType(pt: Zcu.PerThread, child_type: InternPool.Index) Allocator.Error!Type {
   3529     return Type.fromInterned(try pt.intern(.{ .opt_type = child_type }));
   3530 }
   3531 
   3532 pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!Type {
   3533     var canon_info = info;
   3534 
   3535     if (info.flags.size == .c) canon_info.flags.is_allowzero = true;
   3536 
   3537     // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee
   3538     // type, we change it to 0 here. If this causes an assertion trip because the
   3539     // pointee type needs to be resolved more, that needs to be done before calling
   3540     // this ptr() function.
   3541     if (info.flags.alignment != .none and
   3542         info.flags.alignment == Type.fromInterned(info.child).abiAlignment(pt.zcu))
   3543     {
   3544         canon_info.flags.alignment = .none;
   3545     }
   3546 
   3547     switch (info.flags.vector_index) {
   3548         // Canonicalize host_size. If it matches the bit size of the pointee type,
   3549         // we change it to 0 here. If this causes an assertion trip, the pointee type
   3550         // needs to be resolved before calling this ptr() function.
   3551         .none => if (info.packed_offset.host_size != 0) {
   3552             const elem_bit_size = Type.fromInterned(info.child).bitSize(pt.zcu);
   3553             assert(info.packed_offset.bit_offset + elem_bit_size <= info.packed_offset.host_size * 8);
   3554             if (info.packed_offset.host_size * 8 == elem_bit_size) {
   3555                 canon_info.packed_offset.host_size = 0;
   3556             }
   3557         },
   3558         _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
   3559     }
   3560 
   3561     return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info }));
   3562 }
   3563 
   3564 /// Like `ptrType`, but if `info` specifies an `alignment`, first ensures the pointer
   3565 /// child type's alignment is resolved so that an invalid alignment is not used.
   3566 /// In general, prefer this function during semantic analysis.
   3567 pub fn ptrTypeSema(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Zcu.SemaError!Type {
   3568     if (info.flags.alignment != .none) {
   3569         _ = try Type.fromInterned(info.child).abiAlignmentSema(pt);
   3570     }
   3571     return pt.ptrType(info);
   3572 }
   3573 
   3574 pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {
   3575     return pt.ptrType(.{ .child = child_type.toIntern() });
   3576 }
   3577 
   3578 pub fn singleConstPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {
   3579     return pt.ptrType(.{
   3580         .child = child_type.toIntern(),
   3581         .flags = .{
   3582             .is_const = true,
   3583         },
   3584     });
   3585 }
   3586 
   3587 pub fn manyConstPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type {
   3588     return pt.ptrType(.{
   3589         .child = child_type.toIntern(),
   3590         .flags = .{
   3591             .size = .many,
   3592             .is_const = true,
   3593         },
   3594     });
   3595 }
   3596 
   3597 pub fn adjustPtrTypeChild(pt: Zcu.PerThread, ptr_ty: Type, new_child: Type) Allocator.Error!Type {
   3598     var info = ptr_ty.ptrInfo(pt.zcu);
   3599     info.child = new_child.toIntern();
   3600     return pt.ptrType(info);
   3601 }
   3602 
   3603 pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Error!Type {
   3604     const comp = pt.zcu.comp;
   3605     return .fromInterned(try pt.zcu.intern_pool.getFuncType(comp.gpa, comp.io, pt.tid, key));
   3606 }
   3607 
   3608 /// Use this for `anyframe->T` only.
   3609 /// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
   3610 pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type {
   3611     return Type.fromInterned(try pt.intern(.{ .anyframe_type = payload_ty.toIntern() }));
   3612 }
   3613 
   3614 pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {
   3615     return Type.fromInterned(try pt.intern(.{ .error_union_type = .{
   3616         .error_set_type = error_set_ty.toIntern(),
   3617         .payload_type = payload_ty.toIntern(),
   3618     } }));
   3619 }
   3620 
   3621 pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type {
   3622     const names: *const [1]InternPool.NullTerminatedString = &name;
   3623     const comp = pt.zcu.comp;
   3624     return Type.fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names));
   3625 }
   3626 
   3627 /// Sorts `names` in place.
   3628 pub fn errorSetFromUnsortedNames(
   3629     pt: Zcu.PerThread,
   3630     names: []InternPool.NullTerminatedString,
   3631 ) Allocator.Error!Type {
   3632     std.mem.sort(
   3633         InternPool.NullTerminatedString,
   3634         names,
   3635         {},
   3636         InternPool.NullTerminatedString.indexLessThan,
   3637     );
   3638     const comp = pt.zcu.comp;
   3639     const new_ty = try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names);
   3640     return Type.fromInterned(new_ty);
   3641 }
   3642 
   3643 /// Supports only pointers, not pointer-like optionals.
   3644 pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
   3645     const zcu = pt.zcu;
   3646     assert(ty.zigTypeTag(zcu) == .pointer and !ty.isSlice(zcu));
   3647     assert(x != 0 or ty.isAllowzeroPtr(zcu));
   3648     return Value.fromInterned(try pt.intern(.{ .ptr = .{
   3649         .ty = ty.toIntern(),
   3650         .base_addr = .int,
   3651         .byte_offset = x,
   3652     } }));
   3653 }
   3654 
   3655 /// Creates an enum tag value based on the integer tag value.
   3656 pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value {
   3657     if (std.debug.runtime_safety) {
   3658         const tag = ty.zigTypeTag(pt.zcu);
   3659         assert(tag == .@"enum");
   3660     }
   3661     return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
   3662         .ty = ty.toIntern(),
   3663         .int = tag_int,
   3664     } }));
   3665 }
   3666 
   3667 /// Creates an enum tag value based on the field index according to source code
   3668 /// declaration order.
   3669 pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Allocator.Error!Value {
   3670     const ip = &pt.zcu.intern_pool;
   3671     const enum_type = ip.loadEnumType(ty.toIntern());
   3672 
   3673     if (enum_type.values.len == 0) {
   3674         // Auto-numbered fields.
   3675         return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
   3676             .ty = ty.toIntern(),
   3677             .int = try pt.intern(.{ .int = .{
   3678                 .ty = enum_type.tag_ty,
   3679                 .storage = .{ .u64 = field_index },
   3680             } }),
   3681         } }));
   3682     }
   3683 
   3684     return Value.fromInterned(try pt.intern(.{ .enum_tag = .{
   3685         .ty = ty.toIntern(),
   3686         .int = enum_type.values.get(ip)[field_index],
   3687     } }));
   3688 }
   3689 
   3690 pub fn undefValue(pt: Zcu.PerThread, ty: Type) Allocator.Error!Value {
   3691     return Value.fromInterned(try pt.intern(.{ .undef = ty.toIntern() }));
   3692 }
   3693 
   3694 pub fn undefRef(pt: Zcu.PerThread, ty: Type) Allocator.Error!Air.Inst.Ref {
   3695     return Air.internedToRef((try pt.undefValue(ty)).toIntern());
   3696 }
   3697 
   3698 pub fn intValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value {
   3699     if (std.math.cast(u64, x)) |casted| return pt.intValue_u64(ty, casted);
   3700     if (std.math.cast(i64, x)) |casted| return pt.intValue_i64(ty, casted);
   3701     var limbs_buffer: [4]usize = undefined;
   3702     var big_int = BigIntMutable.init(&limbs_buffer, x);
   3703     return pt.intValue_big(ty, big_int.toConst());
   3704 }
   3705 
   3706 pub fn intRef(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Air.Inst.Ref {
   3707     return Air.internedToRef((try pt.intValue(ty, x)).toIntern());
   3708 }
   3709 
   3710 pub fn intValue_big(pt: Zcu.PerThread, ty: Type, x: BigIntConst) Allocator.Error!Value {
   3711     if (ty.toIntern() != .comptime_int_type) {
   3712         const int_info = ty.intInfo(pt.zcu);
   3713         assert(x.fitsInTwosComp(int_info.signedness, int_info.bits));
   3714     }
   3715     return .fromInterned(try pt.intern(.{ .int = .{
   3716         .ty = ty.toIntern(),
   3717         .storage = .{ .big_int = x },
   3718     } }));
   3719 }
   3720 
   3721 pub fn intValue_u64(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value {
   3722     if (ty.toIntern() != .comptime_int_type and x != 0) {
   3723         const int_info = ty.intInfo(pt.zcu);
   3724         const unsigned_bits = int_info.bits - @intFromBool(int_info.signedness == .signed);
   3725         assert(unsigned_bits >= std.math.log2(x) + 1);
   3726     }
   3727     return .fromInterned(try pt.intern(.{ .int = .{
   3728         .ty = ty.toIntern(),
   3729         .storage = .{ .u64 = x },
   3730     } }));
   3731 }
   3732 
   3733 pub fn intValue_i64(pt: Zcu.PerThread, ty: Type, x: i64) Allocator.Error!Value {
   3734     if (ty.toIntern() != .comptime_int_type and x != 0) {
   3735         const int_info = ty.intInfo(pt.zcu);
   3736         const unsigned_bits = int_info.bits - @intFromBool(int_info.signedness == .signed);
   3737         if (x > 0) {
   3738             assert(unsigned_bits >= std.math.log2(x) + 1);
   3739         } else {
   3740             assert(int_info.signedness == .signed);
   3741             assert(unsigned_bits >= std.math.log2_int_ceil(u64, @abs(x)));
   3742         }
   3743     }
   3744     return .fromInterned(try pt.intern(.{ .int = .{
   3745         .ty = ty.toIntern(),
   3746         .storage = .{ .i64 = x },
   3747     } }));
   3748 }
   3749 
   3750 /// Shortcut for calling `intern_pool.getUnion`.
   3751 /// TODO: remove either this or `unionValue`.
   3752 pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!InternPool.Index {
   3753     const comp = pt.zcu.comp;
   3754     return pt.zcu.intern_pool.getUnion(comp.gpa, comp.io, pt.tid, un);
   3755 }
   3756 
   3757 /// TODO: remove either this or `internUnion`.
   3758 pub fn unionValue(pt: Zcu.PerThread, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value {
   3759     const comp = pt.zcu.comp;
   3760     return Value.fromInterned(try pt.zcu.intern_pool.getUnion(comp.gpa, comp.io, pt.tid, .{
   3761         .ty = union_ty.toIntern(),
   3762         .tag = tag.toIntern(),
   3763         .val = val.toIntern(),
   3764     }));
   3765 }
   3766 
   3767 pub fn aggregateValue(pt: Zcu.PerThread, ty: Type, elems: []const InternPool.Index) Allocator.Error!Value {
   3768     for (elems) |elem| {
   3769         if (!Value.fromInterned(elem).isUndef(pt.zcu)) break;
   3770     } else if (elems.len > 0) {
   3771         return pt.undefValue(ty); // all-undef
   3772     }
   3773     return .fromInterned(try pt.intern(.{ .aggregate = .{
   3774         .ty = ty.toIntern(),
   3775         .storage = .{ .elems = elems },
   3776     } }));
   3777 }
   3778 
   3779 /// Asserts that `ty` is either an array or a vector.
   3780 pub fn aggregateSplatValue(pt: Zcu.PerThread, ty: Type, repeated_elem: Value) Allocator.Error!Value {
   3781     switch (ty.zigTypeTag(pt.zcu)) {
   3782         .array, .vector => {},
   3783         else => unreachable,
   3784     }
   3785     if (repeated_elem.isUndef(pt.zcu)) return pt.undefValue(ty);
   3786     return .fromInterned(try pt.intern(.{ .aggregate = .{
   3787         .ty = ty.toIntern(),
   3788         .storage = .{ .repeated_elem = repeated_elem.toIntern() },
   3789     } }));
   3790 }
   3791 
   3792 /// This function casts the float representation down to the representation of the type, potentially
   3793 /// losing data if the representation wasn't correct.
   3794 pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value {
   3795     const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(pt.zcu.getTarget())) {
   3796         16 => .{ .f16 = @as(f16, @floatCast(x)) },
   3797         32 => .{ .f32 = @as(f32, @floatCast(x)) },
   3798         64 => .{ .f64 = @as(f64, @floatCast(x)) },
   3799         80 => .{ .f80 = @as(f80, @floatCast(x)) },
   3800         128 => .{ .f128 = @as(f128, @floatCast(x)) },
   3801         else => unreachable,
   3802     };
   3803     return Value.fromInterned(try pt.intern(.{ .float = .{
   3804         .ty = ty.toIntern(),
   3805         .storage = storage,
   3806     } }));
   3807 }
   3808 
   3809 pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value {
   3810     assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern()));
   3811     return Value.fromInterned(try pt.intern(.{ .opt = .{
   3812         .ty = opt_ty.toIntern(),
   3813         .val = .none,
   3814     } }));
   3815 }
   3816 
   3817 /// `ty` is an integer or a vector of integers.
   3818 pub fn overflowArithmeticTupleType(pt: Zcu.PerThread, ty: Type) !Type {
   3819     const zcu = pt.zcu;
   3820     const comp = zcu.comp;
   3821     const ov_ty: Type = if (ty.zigTypeTag(zcu) == .vector) try pt.vectorType(.{
   3822         .len = ty.vectorLen(zcu),
   3823         .child = .u1_type,
   3824     }) else .u1;
   3825     const tuple_ty = try zcu.intern_pool.getTupleType(comp.gpa, comp.io, pt.tid, .{
   3826         .types = &.{ ty.toIntern(), ov_ty.toIntern() },
   3827         .values = &.{ .none, .none },
   3828     });
   3829     return .fromInterned(tuple_ty);
   3830 }
   3831 
   3832 pub fn smallestUnsignedInt(pt: Zcu.PerThread, max: u64) Allocator.Error!Type {
   3833     return pt.intType(.unsigned, Type.smallestUnsignedBits(max));
   3834 }
   3835 
   3836 /// Returns the smallest possible integer type containing both `min` and
   3837 /// `max`. Asserts that neither value is undef.
   3838 /// TODO: if #3806 is implemented, this becomes trivial
   3839 pub fn intFittingRange(pt: Zcu.PerThread, min: Value, max: Value) !Type {
   3840     const zcu = pt.zcu;
   3841     assert(!min.isUndef(zcu));
   3842     assert(!max.isUndef(zcu));
   3843 
   3844     if (std.debug.runtime_safety) {
   3845         assert(Value.order(min, max, zcu).compare(.lte));
   3846     }
   3847 
   3848     const sign = min.orderAgainstZero(zcu) == .lt;
   3849 
   3850     const min_val_bits = pt.intBitsForValue(min, sign);
   3851     const max_val_bits = pt.intBitsForValue(max, sign);
   3852 
   3853     return pt.intType(
   3854         if (sign) .signed else .unsigned,
   3855         @max(min_val_bits, max_val_bits),
   3856     );
   3857 }
   3858 
   3859 /// Given a value representing an integer, returns the number of bits necessary to represent
   3860 /// this value in an integer. If `sign` is true, returns the number of bits necessary in a
   3861 /// twos-complement integer; otherwise in an unsigned integer.
   3862 /// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true.
   3863 pub fn intBitsForValue(pt: Zcu.PerThread, val: Value, sign: bool) u16 {
   3864     const zcu = pt.zcu;
   3865     assert(!val.isUndef(zcu));
   3866 
   3867     const key = zcu.intern_pool.indexToKey(val.toIntern());
   3868     switch (key.int.storage) {
   3869         .i64 => |x| {
   3870             if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted) + @intFromBool(sign);
   3871             assert(sign);
   3872             // Protect against overflow in the following negation.
   3873             if (x == std.math.minInt(i64)) return 64;
   3874             return Type.smallestUnsignedBits(@as(u64, @intCast(-(x + 1)))) + 1;
   3875         },
   3876         .u64 => |x| {
   3877             return Type.smallestUnsignedBits(x) + @intFromBool(sign);
   3878         },
   3879         .big_int => |big| {
   3880             if (big.positive) return @as(u16, @intCast(big.bitCountAbs() + @intFromBool(sign)));
   3881 
   3882             // Zero is still a possibility, in which case unsigned is fine
   3883             if (big.eqlZero()) return 0;
   3884 
   3885             return @as(u16, @intCast(big.bitCountTwosComp()));
   3886         },
   3887         .lazy_align => |lazy_ty| {
   3888             return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(pt.zcu).toByteUnits() orelse 0) + @intFromBool(sign);
   3889         },
   3890         .lazy_size => |lazy_ty| {
   3891             return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(pt.zcu)) + @intFromBool(sign);
   3892         },
   3893     }
   3894 }
   3895 
   3896 pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type {
   3897     const zcu = pt.zcu;
   3898     const ip = &zcu.intern_pool;
   3899     const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) {
   3900         .unresolved => unreachable,
   3901         .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
   3902         .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const },
   3903     };
   3904     return pt.ptrType(.{
   3905         .child = ty,
   3906         .flags = .{
   3907             .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu))
   3908                 .none
   3909             else
   3910                 alignment,
   3911             .address_space = @"addrspace",
   3912             .is_const = is_const,
   3913         },
   3914     });
   3915 }
   3916 
   3917 /// Intern an `.@"extern"`, creating a corresponding owner `Nav` if necessary.
   3918 /// If necessary, the new `Nav` is queued for codegen.
   3919 /// `key.owner_nav` is ignored and may be `undefined`.
   3920 pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index {
   3921     const zcu = pt.zcu;
   3922     const comp = zcu.comp;
   3923     const result = try zcu.intern_pool.getExtern(comp.gpa, comp.io, pt.tid, key);
   3924     if (result.new_nav.unwrap()) |nav| {
   3925         // This job depends on any resolve_type_fully jobs queued up before it.
   3926         comp.link_prog_node.increaseEstimatedTotalItems(1);
   3927         try comp.queueJob(.{ .link_nav = nav });
   3928         if (comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav);
   3929     }
   3930     return result.index;
   3931 }
   3932 
   3933 // TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`.
   3934 pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment {
   3935     const zcu = pt.zcu;
   3936     const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) {
   3937         .unresolved => unreachable,
   3938         .type_resolved => |r| .{ .fromInterned(r.type), r.alignment },
   3939         .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment },
   3940     };
   3941     if (alignment != .none) return alignment;
   3942     return ty.abiAlignment(zcu);
   3943 }
   3944 
   3945 /// `ty` is a container type requiring resolution (struct, union, or enum).
   3946 /// If `ty` is outdated, it is recreated at a new `InternPool.Index`, which is returned.
   3947 /// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned.
   3948 /// If `ty` is not outdated, that same `InternPool.Index` is returned.
   3949 /// If `ty` has already been replaced by this function, the new index will not be returned again.
   3950 /// Also, if `ty` is an enum, this function will resolve the new type if needed, and the call site
   3951 /// is responsible for checking `[transitive_]failed_analysis` to detect resolution failures.
   3952 pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {
   3953     const zcu = pt.zcu;
   3954     const gpa = zcu.gpa;
   3955     const ip = &zcu.intern_pool;
   3956 
   3957     const anal_unit: AnalUnit = .wrap(.{ .type = ty });
   3958     const outdated = zcu.outdated.swapRemove(anal_unit) or
   3959         zcu.potentially_outdated.swapRemove(anal_unit);
   3960 
   3961     if (outdated) {
   3962         _ = zcu.outdated_ready.swapRemove(anal_unit);
   3963         try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
   3964     }
   3965 
   3966     const ty_key = switch (ip.indexToKey(ty)) {
   3967         .struct_type, .union_type, .enum_type => |key| key,
   3968         else => unreachable,
   3969     };
   3970     const declared_ty_key = switch (ty_key) {
   3971         .reified => unreachable, // never outdated
   3972         .generated_tag => unreachable, // never outdated
   3973         .declared => |d| d,
   3974     };
   3975 
   3976     if (declared_ty_key.zir_index.resolve(ip) == null) {
   3977         // The instruction has been lost -- this type is dead.
   3978         return error.AnalysisFail;
   3979     }
   3980 
   3981     if (!outdated) return ty;
   3982 
   3983     // We will recreate the type at a new `InternPool.Index`.
   3984 
   3985     // Delete old state which is no longer in use. Technically, this is not necessary: these exports,
   3986     // references, etc, will be ignored because the type itself is unreferenced. However, it allows
   3987     // reusing the memory which is currently being used to track this state.
   3988     zcu.deleteUnitExports(anal_unit);
   3989     zcu.deleteUnitReferences(anal_unit);
   3990     zcu.deleteUnitCompileLogs(anal_unit);
   3991     if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
   3992         kv.value.destroy(gpa);
   3993     }
   3994     _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
   3995     zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
   3996 
   3997     if (zcu.comp.debugIncremental()) {
   3998         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit);
   3999         info.last_update_gen = zcu.generation;
   4000         info.deps.clearRetainingCapacity();
   4001     }
   4002 
   4003     switch (ip.indexToKey(ty)) {
   4004         .struct_type => return pt.recreateStructType(ty, declared_ty_key),
   4005         .union_type => return pt.recreateUnionType(ty, declared_ty_key),
   4006         .enum_type => return pt.recreateEnumType(ty, declared_ty_key),
   4007         else => unreachable,
   4008     }
   4009 }
   4010 
   4011 fn recreateStructType(
   4012     pt: Zcu.PerThread,
   4013     old_ty: InternPool.Index,
   4014     key: InternPool.Key.NamespaceType.Declared,
   4015 ) Allocator.Error!InternPool.Index {
   4016     const zcu = pt.zcu;
   4017     const comp = zcu.comp;
   4018     const gpa = comp.gpa;
   4019     const io = comp.io;
   4020     const ip = &zcu.intern_pool;
   4021 
   4022     const inst_info = key.zir_index.resolveFull(ip).?;
   4023     const file = zcu.fileByIndex(inst_info.file);
   4024     const zir = file.zir.?;
   4025 
   4026     assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
   4027     const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
   4028     assert(extended.opcode == .struct_decl);
   4029     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   4030     const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
   4031     var extra_index = extra.end;
   4032 
   4033     const captures_len = if (small.has_captures_len) blk: {
   4034         const captures_len = zir.extra[extra_index];
   4035         extra_index += 1;
   4036         break :blk captures_len;
   4037     } else 0;
   4038     const fields_len = if (small.has_fields_len) blk: {
   4039         const fields_len = zir.extra[extra_index];
   4040         extra_index += 1;
   4041         break :blk fields_len;
   4042     } else 0;
   4043 
   4044     assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew`
   4045 
   4046     const struct_obj = ip.loadStructType(old_ty);
   4047 
   4048     const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{
   4049         .layout = small.layout,
   4050         .fields_len = fields_len,
   4051         .known_non_opv = small.known_non_opv,
   4052         .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
   4053         .any_comptime_fields = small.any_comptime_fields,
   4054         .any_default_inits = small.any_default_inits,
   4055         .inits_resolved = false,
   4056         .any_aligned_fields = small.any_aligned_fields,
   4057         .key = .{ .declared_owned_captures = .{
   4058             .zir_index = key.zir_index,
   4059             .captures = key.captures.owned,
   4060         } },
   4061     }, true)) {
   4062         .wip => |wip| wip,
   4063         .existing => unreachable, // we passed `replace_existing`
   4064     };
   4065     errdefer wip_ty.cancel(ip, pt.tid);
   4066 
   4067     wip_ty.setName(ip, struct_obj.name, struct_obj.name_nav);
   4068     try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index });
   4069     zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index;
   4070     // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive.
   4071     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   4072 
   4073     codegen_type: {
   4074         if (file.mod.?.strip) break :codegen_type;
   4075         // This job depends on any resolve_type_fully jobs queued up before it.
   4076         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   4077         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   4078     }
   4079 
   4080     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   4081     const new_ty = wip_ty.finish(ip, struct_obj.namespace);
   4082     if (inst_info.inst == .main_struct_inst) {
   4083         // This is the root type of a file! Update the reference.
   4084         zcu.setFileRootType(inst_info.file, new_ty);
   4085     }
   4086     return new_ty;
   4087 }
   4088 
   4089 fn recreateUnionType(
   4090     pt: Zcu.PerThread,
   4091     old_ty: InternPool.Index,
   4092     key: InternPool.Key.NamespaceType.Declared,
   4093 ) Allocator.Error!InternPool.Index {
   4094     const zcu = pt.zcu;
   4095     const comp = zcu.comp;
   4096     const gpa = comp.gpa;
   4097     const io = comp.io;
   4098     const ip = &zcu.intern_pool;
   4099 
   4100     const inst_info = key.zir_index.resolveFull(ip).?;
   4101     const file = zcu.fileByIndex(inst_info.file);
   4102     const zir = file.zir.?;
   4103 
   4104     assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
   4105     const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
   4106     assert(extended.opcode == .union_decl);
   4107     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
   4108     const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
   4109     var extra_index = extra.end;
   4110 
   4111     extra_index += @intFromBool(small.has_tag_type);
   4112     const captures_len = if (small.has_captures_len) blk: {
   4113         const captures_len = zir.extra[extra_index];
   4114         extra_index += 1;
   4115         break :blk captures_len;
   4116     } else 0;
   4117     extra_index += @intFromBool(small.has_body_len);
   4118     const fields_len = if (small.has_fields_len) blk: {
   4119         const fields_len = zir.extra[extra_index];
   4120         extra_index += 1;
   4121         break :blk fields_len;
   4122     } else 0;
   4123 
   4124     assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew`
   4125 
   4126     const union_obj = ip.loadUnionType(old_ty);
   4127 
   4128     const namespace_index = union_obj.namespace;
   4129 
   4130     const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{
   4131         .flags = .{
   4132             .layout = small.layout,
   4133             .status = .none,
   4134             .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
   4135                 .tagged
   4136             else if (small.layout != .auto)
   4137                 .none
   4138             else switch (true) { // TODO
   4139                 true => .safety,
   4140                 false => .none,
   4141             },
   4142             .any_aligned_fields = small.any_aligned_fields,
   4143             .requires_comptime = .unknown,
   4144             .assumed_runtime_bits = false,
   4145             .assumed_pointer_aligned = false,
   4146             .alignment = .none,
   4147         },
   4148         .fields_len = fields_len,
   4149         .enum_tag_ty = .none, // set later
   4150         .field_types = &.{}, // set later
   4151         .field_aligns = &.{}, // set later
   4152         .key = .{ .declared_owned_captures = .{
   4153             .zir_index = key.zir_index,
   4154             .captures = key.captures.owned,
   4155         } },
   4156     }, true)) {
   4157         .wip => |wip| wip,
   4158         .existing => unreachable, // we passed `replace_existing`
   4159     };
   4160     errdefer wip_ty.cancel(ip, pt.tid);
   4161 
   4162     wip_ty.setName(ip, union_obj.name, union_obj.name_nav);
   4163     try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index });
   4164     zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
   4165     // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive.
   4166     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   4167 
   4168     codegen_type: {
   4169         if (file.mod.?.strip) break :codegen_type;
   4170         // This job depends on any resolve_type_fully jobs queued up before it.
   4171         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   4172         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   4173     }
   4174 
   4175     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   4176     return wip_ty.finish(ip, namespace_index);
   4177 }
   4178 
   4179 /// This *does* call `Sema.resolveDeclaredEnum`, but errors from it are not propagated.
   4180 /// Call sites are resposible for checking `[transitive_]failed_analysis` after `ensureTypeUpToDate`
   4181 /// returns in order to detect resolution failures.
   4182 fn recreateEnumType(
   4183     pt: Zcu.PerThread,
   4184     old_ty: InternPool.Index,
   4185     key: InternPool.Key.NamespaceType.Declared,
   4186 ) (Allocator.Error || Io.Cancelable)!InternPool.Index {
   4187     const zcu = pt.zcu;
   4188     const comp = zcu.comp;
   4189     const gpa = comp.gpa;
   4190     const io = comp.io;
   4191     const ip = &zcu.intern_pool;
   4192 
   4193     const inst_info = key.zir_index.resolveFull(ip).?;
   4194     const file = zcu.fileByIndex(inst_info.file);
   4195     const zir = file.zir.?;
   4196 
   4197     assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
   4198     const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
   4199     assert(extended.opcode == .enum_decl);
   4200     const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
   4201     const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
   4202     var extra_index = extra.end;
   4203 
   4204     const tag_type_ref = if (small.has_tag_type) blk: {
   4205         const tag_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
   4206         extra_index += 1;
   4207         break :blk tag_type_ref;
   4208     } else .none;
   4209 
   4210     const captures_len = if (small.has_captures_len) blk: {
   4211         const captures_len = zir.extra[extra_index];
   4212         extra_index += 1;
   4213         break :blk captures_len;
   4214     } else 0;
   4215 
   4216     const body_len = if (small.has_body_len) blk: {
   4217         const body_len = zir.extra[extra_index];
   4218         extra_index += 1;
   4219         break :blk body_len;
   4220     } else 0;
   4221 
   4222     const fields_len = if (small.has_fields_len) blk: {
   4223         const fields_len = zir.extra[extra_index];
   4224         extra_index += 1;
   4225         break :blk fields_len;
   4226     } else 0;
   4227 
   4228     const decls_len = if (small.has_decls_len) blk: {
   4229         const decls_len = zir.extra[extra_index];
   4230         extra_index += 1;
   4231         break :blk decls_len;
   4232     } else 0;
   4233 
   4234     assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew`
   4235 
   4236     extra_index += captures_len * 2;
   4237     extra_index += decls_len;
   4238 
   4239     const body = zir.bodySlice(extra_index, body_len);
   4240     extra_index += body.len;
   4241 
   4242     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
   4243     const body_end = extra_index;
   4244     extra_index += bit_bags_count;
   4245 
   4246     const any_values = for (zir.extra[body_end..][0..bit_bags_count]) |bag| {
   4247         if (bag != 0) break true;
   4248     } else false;
   4249 
   4250     const enum_obj = ip.loadEnumType(old_ty);
   4251 
   4252     const namespace_index = enum_obj.namespace;
   4253 
   4254     const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{
   4255         .has_values = any_values,
   4256         .tag_mode = if (small.nonexhaustive)
   4257             .nonexhaustive
   4258         else if (tag_type_ref == .none)
   4259             .auto
   4260         else
   4261             .explicit,
   4262         .fields_len = fields_len,
   4263         .key = .{ .declared_owned_captures = .{
   4264             .zir_index = key.zir_index,
   4265             .captures = key.captures.owned,
   4266         } },
   4267     }, true)) {
   4268         .wip => |wip| wip,
   4269         .existing => unreachable, // we passed `replace_existing`
   4270     };
   4271     var done = true;
   4272     errdefer if (!done) wip_ty.cancel(ip, pt.tid);
   4273 
   4274     wip_ty.setName(ip, enum_obj.name, enum_obj.name_nav);
   4275 
   4276     zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
   4277     // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive.
   4278 
   4279     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   4280     wip_ty.prepare(ip, namespace_index);
   4281     done = true;
   4282 
   4283     Sema.resolveDeclaredEnum(
   4284         pt,
   4285         wip_ty,
   4286         inst_info.inst,
   4287         key.zir_index,
   4288         namespace_index,
   4289         enum_obj.name,
   4290         small,
   4291         body,
   4292         tag_type_ref,
   4293         any_values,
   4294         fields_len,
   4295         zir,
   4296         body_end,
   4297     ) catch |err| switch (err) {
   4298         error.OutOfMemory => |e| return e,
   4299         error.Canceled => |e| return e,
   4300         error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this
   4301     };
   4302 
   4303     return wip_ty.index;
   4304 }
   4305 
   4306 /// Given a namespace, re-scan its declarations from the type definition if they have not
   4307 /// yet been re-scanned on this update.
   4308 /// If the type declaration instruction has been lost, returns `error.AnalysisFail`.
   4309 /// This will effectively short-circuit the caller, which will be semantic analysis of a
   4310 /// guaranteed-unreferenced `AnalUnit`, to trigger a transitive analysis error.
   4311 pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) Zcu.SemaError!void {
   4312     const zcu = pt.zcu;
   4313     const ip = &zcu.intern_pool;
   4314     const namespace = zcu.namespacePtr(namespace_index);
   4315 
   4316     if (namespace.generation == zcu.generation) return;
   4317 
   4318     const Container = enum { @"struct", @"union", @"enum", @"opaque" };
   4319     const container: Container, const full_key = switch (ip.indexToKey(namespace.owner_type)) {
   4320         .struct_type => |k| .{ .@"struct", k },
   4321         .union_type => |k| .{ .@"union", k },
   4322         .enum_type => |k| .{ .@"enum", k },
   4323         .opaque_type => |k| .{ .@"opaque", k },
   4324         else => unreachable, // namespaces are owned by a container type
   4325     };
   4326 
   4327     const key = switch (full_key) {
   4328         .reified, .generated_tag => {
   4329             // Namespace always empty, so up-to-date.
   4330             namespace.generation = zcu.generation;
   4331             return;
   4332         },
   4333         .declared => |d| d,
   4334     };
   4335 
   4336     // Namespace outdated -- re-scan the type if necessary.
   4337 
   4338     const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
   4339     const file = zcu.fileByIndex(inst_info.file);
   4340     const zir = file.zir.?;
   4341 
   4342     assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
   4343     const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
   4344 
   4345     const decls = switch (container) {
   4346         .@"struct" => decls: {
   4347             assert(extended.opcode == .struct_decl);
   4348             const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   4349             const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
   4350             var extra_index = extra.end;
   4351             const captures_len = if (small.has_captures_len) blk: {
   4352                 const captures_len = zir.extra[extra_index];
   4353                 extra_index += 1;
   4354                 break :blk captures_len;
   4355             } else 0;
   4356             extra_index += @intFromBool(small.has_fields_len);
   4357             const decls_len = if (small.has_decls_len) blk: {
   4358                 const decls_len = zir.extra[extra_index];
   4359                 extra_index += 1;
   4360                 break :blk decls_len;
   4361             } else 0;
   4362             extra_index += captures_len * 2;
   4363             if (small.has_backing_int) {
   4364                 const backing_int_body_len = zir.extra[extra_index];
   4365                 extra_index += 1; // backing_int_body_len
   4366                 if (backing_int_body_len == 0) {
   4367                     extra_index += 1; // backing_int_ref
   4368                 } else {
   4369                     extra_index += backing_int_body_len; // backing_int_body_inst
   4370                 }
   4371             }
   4372             break :decls zir.bodySlice(extra_index, decls_len);
   4373         },
   4374         .@"union" => decls: {
   4375             assert(extended.opcode == .union_decl);
   4376             const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
   4377             const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
   4378             var extra_index = extra.end;
   4379             extra_index += @intFromBool(small.has_tag_type);
   4380             const captures_len = if (small.has_captures_len) blk: {
   4381                 const captures_len = zir.extra[extra_index];
   4382                 extra_index += 1;
   4383                 break :blk captures_len;
   4384             } else 0;
   4385             extra_index += @intFromBool(small.has_body_len);
   4386             extra_index += @intFromBool(small.has_fields_len);
   4387             const decls_len = if (small.has_decls_len) blk: {
   4388                 const decls_len = zir.extra[extra_index];
   4389                 extra_index += 1;
   4390                 break :blk decls_len;
   4391             } else 0;
   4392             extra_index += captures_len * 2;
   4393             break :decls zir.bodySlice(extra_index, decls_len);
   4394         },
   4395         .@"enum" => decls: {
   4396             assert(extended.opcode == .enum_decl);
   4397             const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
   4398             const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
   4399             var extra_index = extra.end;
   4400             extra_index += @intFromBool(small.has_tag_type);
   4401             const captures_len = if (small.has_captures_len) blk: {
   4402                 const captures_len = zir.extra[extra_index];
   4403                 extra_index += 1;
   4404                 break :blk captures_len;
   4405             } else 0;
   4406             extra_index += @intFromBool(small.has_body_len);
   4407             extra_index += @intFromBool(small.has_fields_len);
   4408             const decls_len = if (small.has_decls_len) blk: {
   4409                 const decls_len = zir.extra[extra_index];
   4410                 extra_index += 1;
   4411                 break :blk decls_len;
   4412             } else 0;
   4413             extra_index += captures_len * 2;
   4414             break :decls zir.bodySlice(extra_index, decls_len);
   4415         },
   4416         .@"opaque" => decls: {
   4417             assert(extended.opcode == .opaque_decl);
   4418             const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
   4419             const extra = zir.extraData(Zir.Inst.OpaqueDecl, extended.operand);
   4420             var extra_index = extra.end;
   4421             const captures_len = if (small.has_captures_len) blk: {
   4422                 const captures_len = zir.extra[extra_index];
   4423                 extra_index += 1;
   4424                 break :blk captures_len;
   4425             } else 0;
   4426             const decls_len = if (small.has_decls_len) blk: {
   4427                 const decls_len = zir.extra[extra_index];
   4428                 extra_index += 1;
   4429                 break :blk decls_len;
   4430             } else 0;
   4431             extra_index += captures_len * 2;
   4432             break :decls zir.bodySlice(extra_index, decls_len);
   4433         },
   4434     };
   4435 
   4436     try pt.scanNamespace(namespace_index, decls);
   4437     namespace.generation = zcu.generation;
   4438 }
   4439 
   4440 pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index {
   4441     const ptr_ty = (try pt.ptrTypeSema(.{
   4442         .child = pt.zcu.intern_pool.typeOf(val),
   4443         .flags = .{
   4444             .alignment = .none,
   4445             .is_const = true,
   4446             .address_space = .generic,
   4447         },
   4448     })).toIntern();
   4449     return pt.intern(.{ .ptr = .{
   4450         .ty = ptr_ty,
   4451         .base_addr = .{ .uav = .{
   4452             .val = val,
   4453             .orig_ty = ptr_ty,
   4454         } },
   4455         .byte_offset = 0,
   4456     } });
   4457 }
   4458 
   4459 pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void {
   4460     const zcu = pt.zcu;
   4461     const gpa = zcu.comp.gpa;
   4462     try zcu.intern_pool.addDependency(gpa, unit, dependee);
   4463     if (zcu.comp.debugIncremental()) {
   4464         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit);
   4465         try info.deps.append(gpa, dependee);
   4466     }
   4467 }
   4468 
   4469 pub const RunCodegenError = Io.Cancelable || error{AlreadyReported};
   4470 
   4471 /// Performs code generation, which comes after `Sema` but before `link` in the pipeline. This part
   4472 /// of the pipeline is self-contained and can usually be run concurrently with other components.
   4473 ///
   4474 /// This function is called asynchronously by `Zcu.CodegenTaskPool.start` and awaited by the linker.
   4475 /// However, if the codegen backend does not support `Zcu.Feature.separate_thread`, then
   4476 /// `Compilation.processOneJob` will immediately await the result of the linker task, meaning the
   4477 /// pipeline becomes effectively single-threaded.
   4478 pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) RunCodegenError!codegen.AnyMir {
   4479     const zcu = pt.zcu;
   4480     const comp = zcu.comp;
   4481     const io = comp.io;
   4482 
   4483     crash_report.CodegenFunc.start(zcu, func_index);
   4484     defer crash_report.CodegenFunc.stop(func_index);
   4485 
   4486     var timer = comp.startTimer();
   4487 
   4488     const codegen_result = runCodegenInner(pt, func_index, air);
   4489 
   4490     if (timer.finish()) |ns_codegen| report_time: {
   4491         const ip = &zcu.intern_pool;
   4492         const nav = ip.indexToKey(func_index).func.owner_nav;
   4493         const zir_decl = ip.getNav(nav).srcInst(ip);
   4494         comp.mutex.lockUncancelable(io);
   4495         defer comp.mutex.unlock(io);
   4496         const tr = &zcu.comp.time_report.?;
   4497         tr.stats.cpu_ns_codegen += ns_codegen;
   4498         const gop = tr.decl_codegen_ns.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) {
   4499             error.OutOfMemory => {
   4500                 comp.setAllocFailure();
   4501                 break :report_time;
   4502             },
   4503         };
   4504         if (!gop.found_existing) gop.value_ptr.* = 0;
   4505         gop.value_ptr.* += ns_codegen;
   4506     }
   4507 
   4508     if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) {
   4509         // Decremented to 0, so all done.
   4510         zcu.codegen_prog_node.end();
   4511         zcu.codegen_prog_node = .none;
   4512     }
   4513 
   4514     return codegen_result catch |err| {
   4515         switch (err) {
   4516             error.OutOfMemory => comp.setAllocFailure(),
   4517             error.CodegenFail => zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav),
   4518             error.NoLinkFile => assert(comp.bin_file == null),
   4519             error.BackendDoesNotProduceMir => switch (target_util.zigBackend(
   4520                 &zcu.root_mod.resolved_target.result,
   4521                 comp.config.use_llvm,
   4522             )) {
   4523                 else => unreachable, // assertion failure
   4524                 .stage2_spirv,
   4525                 .stage2_llvm,
   4526                 => {},
   4527             },
   4528             error.Canceled => |e| return e,
   4529         }
   4530         return error.AlreadyReported;
   4531     };
   4532 }
   4533 fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{
   4534     OutOfMemory,
   4535     Canceled,
   4536     CodegenFail,
   4537     NoLinkFile,
   4538     BackendDoesNotProduceMir,
   4539 }!codegen.AnyMir {
   4540     const zcu = pt.zcu;
   4541     const gpa = zcu.gpa;
   4542     const ip = &zcu.intern_pool;
   4543     const comp = zcu.comp;
   4544 
   4545     const nav = zcu.funcInfo(func_index).owner_nav;
   4546     const fqn = ip.getNav(nav).fqn;
   4547 
   4548     const codegen_prog_node = zcu.codegen_prog_node.start(fqn.toSlice(ip), 0);
   4549     defer codegen_prog_node.end();
   4550 
   4551     if (codegen.legalizeFeatures(pt, nav)) |features| {
   4552         try air.legalize(pt, features);
   4553     }
   4554 
   4555     var liveness: ?Air.Liveness = if (codegen.wantsLiveness(pt, nav))
   4556         try .analyze(zcu, air.*, ip)
   4557     else
   4558         null;
   4559     defer if (liveness) |*l| l.deinit(gpa);
   4560 
   4561     if (build_options.enable_debug_extensions and comp.verbose_air) p: {
   4562         const io = comp.io;
   4563         const stderr = try io.lockStderr(&.{}, null);
   4564         defer io.unlockStderr();
   4565         printVerboseAir(pt, liveness, fqn, air, &stderr.file_writer.interface) catch |err| switch (err) {
   4566             error.WriteFailed => switch (stderr.file_writer.err.?) {
   4567                 error.Canceled => |e| return e,
   4568                 else => break :p,
   4569             },
   4570         };
   4571     }
   4572 
   4573     if (std.debug.runtime_safety) verify_liveness: {
   4574         var verify: Air.Liveness.Verify = .{
   4575             .gpa = gpa,
   4576             .zcu = zcu,
   4577             .air = air.*,
   4578             .liveness = liveness orelse break :verify_liveness,
   4579             .intern_pool = ip,
   4580         };
   4581         defer verify.deinit();
   4582 
   4583         verify.verify() catch |err| switch (err) {
   4584             error.OutOfMemory => return error.OutOfMemory,
   4585             else => return zcu.codegenFail(nav, "invalid liveness: {t}", .{err}),
   4586         };
   4587     }
   4588 
   4589     // The LLVM backend is special, because we only need to do codegen. There is no equivalent to the
   4590     // "emit" step because LLVM does not support incremental linking. Our linker (LLD or self-hosted)
   4591     // will just see the ZCU object file which LLVM ultimately emits.
   4592     if (zcu.llvm_object) |llvm_object| {
   4593         assert(zcu.pending_codegen_jobs.load(.monotonic) == 2); // only one codegen at a time (but the value is 2 because 1 is the base)
   4594         try llvm_object.updateFunc(pt, func_index, air, &liveness);
   4595         return error.BackendDoesNotProduceMir;
   4596     }
   4597 
   4598     const lf = comp.bin_file orelse return error.NoLinkFile;
   4599 
   4600     // Just like LLVM, the SPIR-V backend can't multi-threaded due to SPIR-V design limitations.
   4601     if (lf.cast(.spirv)) |spirv_file| {
   4602         assert(zcu.pending_codegen_jobs.load(.monotonic) == 2); // only one codegen at a time (but the value is 2 because 1 is the base)
   4603         spirv_file.updateFunc(pt, func_index, air, &liveness) catch |err| {
   4604             switch (err) {
   4605                 error.OutOfMemory => comp.link_diags.setAllocFailure(),
   4606             }
   4607             return error.CodegenFail;
   4608         };
   4609         return error.BackendDoesNotProduceMir;
   4610     }
   4611 
   4612     return codegen.generateFunction(lf, pt, zcu.navSrcLoc(nav), func_index, air, &liveness) catch |err| switch (err) {
   4613         error.OutOfMemory,
   4614         error.CodegenFail,
   4615         => |e| return e,
   4616         error.Overflow,
   4617         error.RelocationNotByteAligned,
   4618         => return zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)}),
   4619     };
   4620 }
   4621 
   4622 fn printVerboseAir(
   4623     pt: Zcu.PerThread,
   4624     liveness: ?Air.Liveness,
   4625     fqn: InternPool.NullTerminatedString,
   4626     air: *const Air,
   4627     w: *Io.Writer,
   4628 ) Io.Writer.Error!void {
   4629     const zcu = pt.zcu;
   4630     const ip = &zcu.intern_pool;
   4631     try w.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)});
   4632     try air.write(w, pt, liveness);
   4633     try w.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)});
   4634 }