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