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