1//! Zig Compilation Unit
2//!
3//! Compilation of all Zig source code is represented by one `Zcu`.
4//!
5//! Each `Compilation` has exactly one or zero `Zcu`, depending on whether
6//! there is or is not any zig source code, respectively.
7const Zcu = @This();
8const builtin = @import("builtin");
9
10const std = @import("std");
11const Io = std.Io;
12const Writer = std.Io.Writer;
13const mem = std.mem;
14const Allocator = std.mem.Allocator;
15const assert = std.debug.assert;
16const log = std.log.scoped(.zcu);
17const deps_log = std.log.scoped(.zcu_deps);
18const refs_log = std.log.scoped(.zcu_refs);
19const BigIntConst = std.math.big.int.Const;
20const BigIntMutable = std.math.big.int.Mutable;
21const Target = std.Target;
22const Ast = std.zig.Ast;
23
24const Compilation = @import("Compilation.zig");
25const Cache = std.Build.Cache;
26pub const Value = @import("Value.zig");
27pub const Type = @import("Type.zig");
28const Module = @import("Module.zig");
29const link = @import("link.zig");
30const Air = @import("Air.zig");
31const Zir = std.zig.Zir;
32const tracy = @import("tracy.zig");
33const AstGen = std.zig.AstGen;
34const Sema = @import("Sema.zig");
35const target_util = @import("target.zig");
36const build_options = @import("build_options");
37const InternPool = @import("InternPool.zig");
38const Alignment = InternPool.Alignment;
39const AnalUnit = InternPool.AnalUnit;
40const BuiltinFn = std.zig.BuiltinFn;
41const codegen = @import("codegen.zig");
42const LlvmObject = @import("codegen/llvm.zig").Object;
43const dev = @import("dev.zig");
44const Zoir = std.zig.Zoir;
45const ZonGen = std.zig.ZonGen;
46
47comptime {
48 @setEvalBranchQuota(4000);
49 for (
50 @typeInfo(Zir.Inst.Ref).@"enum".field_names,
51 @typeInfo(Air.Inst.Ref).@"enum".field_names,
52 @typeInfo(InternPool.Index).@"enum".field_names,
53 ) |zir_field_name, air_field_name, ip_field_name| {
54 assert(mem.eql(u8, zir_field_name, ip_field_name));
55 assert(mem.eql(u8, air_field_name, ip_field_name));
56 }
57}
58
59/// General-purpose allocator. Used for both temporary and long-term storage.
60gpa: Allocator,
61comp: *Compilation,
62/// If the ZCU is emitting an LLVM object (i.e. we are using the LLVM backend), then this is the
63/// `LlvmObject` we are emitting to.
64llvm_object: ?LlvmObject.Ptr,
65
66/// Pointer to externally managed resource.
67root_mod: *Module,
68/// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which
69/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
70main_mod: *Module,
71std_mod: *Module,
72sema_prog_node: std.Progress.Node = .none,
73codegen_prog_node: std.Progress.Node = .none,
74/// The number of codegen jobs which are pending or in-progress. Whichever thread drops this value
75/// to 0 is responsible for ending `codegen_prog_node`. While semantic analysis is happening, this
76/// value bottoms out at 1 instead of 0, to ensure that it can only drop to 0 after analysis is
77/// completed (since semantic analysis could trigger more codegen work).
78pending_codegen_jobs: std.atomic.Value(u32) = .init(0),
79
80/// This is the progress node *under* `sema_prog_node` which is currently running.
81/// When we have to pause to analyze something else, we just temporarily rename this node.
82/// Eventually, when we thread semantic analysis, we will want one of these per thread.
83cur_sema_prog_node: std.Progress.Node = .none,
84
85/// Used by AstGen worker to load and store ZIR cache.
86global_zir_cache: Cache.Directory,
87/// Used by AstGen worker to load and store ZIR cache.
88local_zir_cache: Cache.Directory,
89
90/// This is where all `Export` values are stored. Not all values here are necessarily valid exports;
91/// to enumerate all exports, `single_exports` and `multi_exports` must be consulted.
92all_exports: std.ArrayList(Export) = .empty,
93/// This is a list of free indices in `all_exports`. These indices may be reused by exports from
94/// future semantic analysis.
95free_exports: std.ArrayList(Export.Index) = .empty,
96/// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of
97/// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit`
98/// whose analysis triggered the export.
99single_exports: std.array_hash_map.Auto(AnalUnit, Export.Index) = .empty,
100/// Like `single_exports`, but for `AnalUnit`s which perform multiple exports.
101/// The exports are `all_exports.items[index..][0..len]`.
102multi_exports: std.array_hash_map.Auto(AnalUnit, extern struct {
103 index: u32,
104 len: u32,
105}) = .{},
106
107/// Key is the digest returned by `Builtin.hash`; value is the corresponding module.
108builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Module) = .empty,
109
110/// Populated as soon as the `Compilation` is created. Guaranteed to contain all modules, even builtin ones.
111/// Modules whose root file is not a Zig or ZON file have the value `.none`.
112module_roots: std.array_hash_map.Auto(*Module, File.Index.Optional) = .empty,
113
114/// The set of all the Zig source files in the Zig Compilation Unit. Tracked in
115/// order to iterate over it and check which source files have been modified on
116/// the file system when an update is requested, as well as to cache `@import`
117/// results.
118///
119/// Always accessed through `ImportTableAdapter`, where keys are fully resolved
120/// file paths in order to ensure files are properly deduplicated. This table owns
121/// the keysand values.
122///
123/// Protected by Compilation's mutex.
124///
125/// Not serialized. This state is reconstructed during the first call to
126/// `Compilation.update` of the process for a given `Compilation`.
127import_table: std.array_hash_map.Custom(
128 File.Index,
129 void,
130 struct {
131 pub const hash = @compileError("all accesses should be through ImportTableAdapter");
132 pub const eql = @compileError("all accesses should be through ImportTableAdapter");
133 },
134 true, // This is necessary! Without it, the map tries to use its Context to rehash. #21918
135) = .empty,
136
137/// The set of all files in `import_table` which are "alive" this update, meaning
138/// they are reachable by traversing imports starting from an analysis root. This
139/// is usually all files in `import_table`, but some could be omitted if an incremental
140/// update removes an import, or if a module specified on the CLI is never imported.
141/// Reconstructed on every update, after AstGen and before Sema.
142/// Value is why the file is alive.
143alive_files: std.array_hash_map.Auto(File.Index, File.Reference) = .empty,
144
145/// If this is populated, a "file exists in multiple modules" error should be emitted.
146/// This causes file errors to not be shown, because we don't really know which files
147/// should be alive (because the user has messed up their imports somewhere!).
148/// Cleared and recomputed every update, after AstGen and before Sema.
149multi_module_err: ?struct {
150 file: File.Index,
151 modules: [2]*Module,
152 refs: [2]File.Reference,
153} = null,
154
155/// The set of all the files which have been loaded with `@embedFile` in the Module.
156/// We keep track of this in order to iterate over it and check which files have been
157/// modified on the file system when an update is requested, as well as to cache
158/// `@embedFile` results.
159///
160/// Like `import_table`, this is accessed through `EmbedTableAdapter`, so that it is keyed
161/// on the `Compilation.Path` of the `EmbedFile`.
162///
163/// This table owns all of the `*EmbedFile` memory, which is allocated into gpa.
164embed_table: std.array_hash_map.Custom(
165 *EmbedFile,
166 void,
167 struct {
168 pub const hash = @compileError("all accesses should be through EmbedTableAdapter");
169 pub const eql = @compileError("all accesses should be through EmbedTableAdapter");
170 },
171 true, // This is necessary! Without it, the map tries to use its Context to rehash. #21918
172) = .empty,
173
174/// Stores all Type and Value objects.
175/// The idea is that this will be periodically garbage-collected, but such logic
176/// is not yet implemented.
177intern_pool: InternPool = .empty,
178
179/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis
180/// (index 0), and non-`null` for all others.
181analysis_in_progress: std.array_hash_map.Auto(AnalUnit, ?*const DependencyReason) = .empty,
182/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
183failed_analysis: std.array_hash_map.Auto(AnalUnit, *ErrorMsg) = .empty,
184/// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed.
185transitive_failed_analysis: std.array_hash_map.Auto(
186 AnalUnit,
187 if (build_options.enable_debug_extensions) TransitiveFailureReason else void,
188) = .empty,
189/// This `Nav` succeeded analysis, but failed codegen.
190/// This may be a simple "value" `Nav`, or it may be a function.
191/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
192/// While multiple threads are active (most of the time!), this is guarded by `zcu.comp.mutex`, as
193/// codegen and linking run on a separate thread.
194failed_codegen: std.array_hash_map.Auto(InternPool.Nav.Index, *ErrorMsg) = .empty,
195failed_types: std.array_hash_map.Auto(InternPool.Index, *ErrorMsg) = .empty,
196
197/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one
198/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible
199/// between compilations. So, instead of (for instance) defining where the dependency loop "starts",
200/// this map simply exists to allow easily iterating all dependency loops exactly once.
201dependency_loops: std.array_hash_map.Auto(AnalUnit, void) = .empty,
202/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so.
203/// All units in here form loops. To iterate loops, see `dependency_loops`.
204dependency_loop_nodes: std.array_hash_map.Auto(AnalUnit, struct {
205 unit: AnalUnit,
206 reason: DependencyReason,
207}) = .empty,
208
209/// Keep track of `@compileLog`s per `AnalUnit`.
210/// We track the source location of the first `@compileLog` call, and all logged lines as a linked list.
211/// The list is singly linked, but we do track its tail for fast appends (optimizing many logs in one unit).
212compile_logs: std.array_hash_map.Auto(AnalUnit, extern struct {
213 base_node_inst: InternPool.TrackedInst.Index,
214 node_offset: Ast.Node.Offset,
215 first_line: CompileLogLine.Index,
216 last_line: CompileLogLine.Index,
217 pub fn src(self: @This()) LazySrcLoc {
218 return .{
219 .base_node_inst = self.base_node_inst,
220 .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset),
221 };
222 }
223}) = .empty,
224compile_log_lines: std.ArrayList(CompileLogLine) = .empty,
225free_compile_log_lines: std.ArrayList(CompileLogLine.Index) = .empty,
226/// This tracks files which triggered errors when generating AST/ZIR/ZOIR.
227/// If not `null`, the value is a retryable error (the file status is guaranteed
228/// to be `.retryable_failure`). Otherwise, the file status is `.astgen_failure`
229/// or `.success`, and there are ZIR/ZOIR errors which should be printed.
230/// We just store a `[]u8` instead of a full `*ErrorMsg`, because the source
231/// location is always the entire file. The `[]u8` memory is owned by the map
232/// and allocated into `gpa`.
233failed_files: std.array_hash_map.Auto(File.Index, ?[]u8) = .empty,
234/// AstGen is not aware of modules, and so cannot determine whether an import
235/// string makes sense. That is the job of a traversal after AstGen.
236///
237/// There are several ways in which an import can fail:
238///
239/// * It is an import of a file which does not exist. This case is not handled
240/// by this field, but with a `failed_files` entry on the *imported* file.
241/// * It is an import of a module which does not exist in the current module's
242/// dependency table. This happens at `Sema` time, so is not tracked by this
243/// field.
244/// * It is an import which reaches outside of the current module's root
245/// directory. This is tracked by this field.
246/// * It is an import which reaches into an "illegal import directory". Right now,
247/// the only such directory is 'global_cache/b/', but in general, these are
248/// directories the compiler treats specially. This is tracked by this field.
249///
250/// This is a flat array containing all of the relevant errors. It is cleared and
251/// recomputed on every update. The errors here are fatal, i.e. they block any
252/// semantic analysis this update.
253///
254/// Allocated into gpa.
255failed_imports: std.ArrayList(struct {
256 file_index: File.Index,
257 import_string: Zir.NullTerminatedString,
258 import_token: Ast.TokenIndex,
259 kind: enum { file_outside_module_root, illegal_zig_import },
260}) = .empty,
261failed_exports: std.array_hash_map.Auto(Export.Index, *ErrorMsg) = .empty,
262/// If analysis failed due to a cimport error, the corresponding Clang errors
263/// are stored here.
264cimport_errors: std.array_hash_map.Auto(AnalUnit, std.zig.ErrorBundle) = .empty,
265
266/// Maximum amount of distinct error values, set by --error-limit
267error_limit: ErrorInt,
268
269/// In safe builds, `Type.assertHasLayout` may be called cross-thread, so this lock
270/// guards accesses to `outdated` and `potentially_outdated`. In unsafe builds, the
271/// lock is not needed and is compiled out.
272outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void = if (std.debug.runtime_safety) .init,
273/// Value is the number of PO dependencies of this AnalUnit.
274/// This value will decrease as we perform semantic analysis to learn what is outdated.
275/// If any of these PO deps is outdated, this value will be moved to `outdated`.
276potentially_outdated: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
277/// Value is the number of PO dependencies of this AnalUnit.
278/// Once this value drops to 0, the AnalUnit is a candidate for re-analysis.
279outdated: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
280/// This is the set of all `AnalUnit`s in `outdated` whose PO dependency count is 0.
281/// Such `AnalUnit`s are ready for immediate re-analysis.
282/// See `findOutdatedToAnalyze` for details.
283outdated_ready: struct {
284 /// These are separate from other units because it allows `findOutdatedToAnalyze` to prioritize
285 /// functions, which is useful because it means they will be sent to codegen more quickly.
286 funcs: std.array_hash_map.Auto(InternPool.Index, void),
287 /// Does not contain `.func` units.
288 other: std.array_hash_map.Auto(AnalUnit, void),
289} = .{ .funcs = .empty, .other = .empty },
290/// This contains a list of AnalUnit whose analysis or codegen failed, but the
291/// failure was something like running out of disk space, and trying again may
292/// succeed. On the next update, we will flush this list, marking all members of
293/// it as outdated.
294retryable_failures: std.ArrayList(AnalUnit) = .empty,
295
296/// These are the modules which we initially queue for analysis in `Compilation.update`.
297/// `resolveReferences` will use these as the root of its reachability traversal.
298analysis_roots_buffer: [5]*Module,
299analysis_roots_len: usize = 0,
300/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
301/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
302/// Allocated into `gpa`.
303resolved_references: ?std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) = null,
304
305/// If `true`, then semantic analysis must not occur on this update due to AstGen errors.
306/// Essentially the entire pipeline after AstGen, including Sema, codegen, and link, is skipped.
307/// Reset to `false` at the start of each update in `Compilation.update`.
308skip_analysis_this_update: bool = false,
309
310test_functions: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty,
311
312global_assembly: std.array_hash_map.Auto(AnalUnit, []u8) = .empty,
313
314/// Key is the `AnalUnit` *performing* the reference. This representation allows
315/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
316/// Value is index into `all_references` of the first reference triggered by the unit.
317/// The `next` field on the `Reference` forms a linked list of all references
318/// triggered by the key `AnalUnit`.
319reference_table: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
320all_references: std.ArrayList(Reference) = .empty,
321/// Freelist of indices in `all_references`.
322free_references: std.ArrayList(u32) = .empty,
323
324inline_reference_frames: std.ArrayList(InlineReferenceFrame) = .empty,
325free_inline_reference_frames: std.ArrayList(InlineReferenceFrame.Index) = .empty,
326
327/// Key is the `AnalUnit` *performing* the reference. This representation allows
328/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
329/// Value is index into `all_type_reference` of the first reference triggered by the unit.
330/// The `next` field on the `TypeReference` forms a linked list of all type references
331/// triggered by the key `AnalUnit`.
332type_reference_table: std.array_hash_map.Auto(AnalUnit, u32) = .empty,
333all_type_references: std.ArrayList(TypeReference) = .empty,
334/// Freelist of indices in `all_type_references`.
335free_type_references: std.ArrayList(u32) = .empty,
336
337/// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = s })`, where `s` depends on the element.
338std_lang_decl_values: StdLangDecl.Memoized = .initFill(.none),
339
340incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void =
341 if (build_options.enable_debug_extensions) .init else {},
342
343/// Times semantic analysis of the current `AnalUnit`. When we pause to analyze a different unit,
344/// this timer must be temporarily paused and resumed later.
345cur_analysis_timer: ?Compilation.Timer = null,
346
347codegen_task_pool: CodegenTaskPool,
348
349generation: u32 = 0,
350
351pub const DependencyReason = struct {
352 src: LazySrcLoc,
353 /// Only populated if this is for a `.type_layout` unit.
354 type_layout_reason: Sema.type_resolution.LayoutResolveReason,
355};
356
357/// These are not required for anything, but when the compiler is built with debug extensions, we
358/// store these in `Zcu.transitive_failed_analysis` and surface them in the incremental debug server
359/// (see `src/IncrementalDebugServer.zig`) because they are a useful debugging aid for bugs in
360/// incremental compilation.
361pub const TransitiveFailureReason = union(enum) {
362 astgen_error,
363 dependency_loop,
364 lost_tracking: InternPool.TrackedInst.Index,
365 failed_unit: AnalUnit,
366 func_nav_val_changed: InternPool.Index,
367};
368
369pub const IncrementalDebugState = struct {
370 /// All container types in the ZCU, even dead ones.
371 /// Value is the generation the type was created on.
372 types: std.array_hash_map.Auto(InternPool.Index, u32),
373 /// All `Nav`s in the ZCU, even dead ones.
374 /// Value is the generation the `Nav` was created on.
375 navs: std.array_hash_map.Auto(InternPool.Nav.Index, u32),
376 /// All `AnalUnit`s in the ZCU, even dead ones.
377 units: std.array_hash_map.Auto(AnalUnit, UnitInfo),
378
379 pub const init: IncrementalDebugState = .{
380 .types = .empty,
381 .navs = .empty,
382 .units = .empty,
383 };
384 pub fn deinit(ids: *IncrementalDebugState, gpa: Allocator) void {
385 for (ids.units.values()) |*unit_info| {
386 unit_info.deps.deinit(gpa);
387 }
388 ids.types.deinit(gpa);
389 ids.navs.deinit(gpa);
390 ids.units.deinit(gpa);
391 }
392
393 pub const UnitInfo = struct {
394 last_update_gen: u32,
395 /// This information isn't easily recoverable from `InternPool`'s dependency storage format.
396 deps: std.ArrayList(InternPool.Dependee),
397 };
398 pub fn getUnitInfo(ids: *IncrementalDebugState, gpa: Allocator, unit: AnalUnit) Allocator.Error!*UnitInfo {
399 const gop = try ids.units.getOrPut(gpa, unit);
400 if (!gop.found_existing) gop.value_ptr.* = .{
401 .last_update_gen = std.math.maxInt(u32),
402 .deps = .empty,
403 };
404 return gop.value_ptr;
405 }
406 pub fn newType(ids: *IncrementalDebugState, zcu: *Zcu, ty: InternPool.Index) Allocator.Error!void {
407 try ids.types.putNoClobber(zcu.gpa, ty, zcu.generation);
408 }
409 pub fn newNav(ids: *IncrementalDebugState, zcu: *Zcu, nav: InternPool.Nav.Index) Allocator.Error!void {
410 try ids.navs.putNoClobber(zcu.gpa, nav, zcu.generation);
411 }
412};
413
414pub const PerThread = @import("Zcu/PerThread.zig");
415
416pub const ImportTableAdapter = struct {
417 zcu: *const Zcu,
418 pub fn hash(ctx: ImportTableAdapter, path: Compilation.Path) u32 {
419 _ = ctx;
420 return @truncate(std.hash.Wyhash.hash(@backingInt(path.root), path.sub_path));
421 }
422 pub fn eql(ctx: ImportTableAdapter, a_path: Compilation.Path, b_file: File.Index, b_index: usize) bool {
423 _ = b_index;
424 const b_path = ctx.zcu.fileByIndex(b_file).path;
425 return a_path.root == b_path.root and mem.eql(u8, a_path.sub_path, b_path.sub_path);
426 }
427};
428
429pub const EmbedTableAdapter = struct {
430 pub fn hash(ctx: EmbedTableAdapter, path: Compilation.Path) u32 {
431 _ = ctx;
432 return @truncate(std.hash.Wyhash.hash(@backingInt(path.root), path.sub_path));
433 }
434 pub fn eql(ctx: EmbedTableAdapter, a_path: Compilation.Path, b_file: *EmbedFile, b_index: usize) bool {
435 _ = ctx;
436 _ = b_index;
437 const b_path = b_file.path;
438 return a_path.root == b_path.root and mem.eql(u8, a_path.sub_path, b_path.sub_path);
439 }
440};
441
442/// Names of declarations in `std.lang` whose values are memoized in a `StdLangDecl.Memoized`.
443/// The name must exactly match the declaration name, as comptime logic is used to compute the namespace accesses.
444/// Parent namespaces must be before their children in this enum. For instance, `.Type` must be before `.@"Type.Fn"`.
445/// Additionally, parent namespaces must be resolved in the same stage as their children; see `StdLangDecl.stage`.
446pub const StdLangDecl = enum {
447 Signedness,
448 AddressSpace,
449 CallingConvention,
450 returnError,
451 StackTrace,
452 SourceLocation,
453 CallModifier,
454 AtomicOrder,
455 AtomicRmwOp,
456 ReduceOp,
457 FloatMode,
458 PrefetchOptions,
459 ExportOptions,
460 ExternOptions,
461 BranchHint,
462
463 Type,
464 @"Type.Fn",
465 @"Type.Fn.ParamAttributes",
466 @"Type.Fn.Attributes",
467 @"Type.Int",
468 @"Type.Float",
469 @"Type.Pointer",
470 @"Type.Pointer.Size",
471 @"Type.Pointer.Attributes",
472 @"Type.Array",
473 @"Type.Vector",
474 @"Type.Optional",
475 @"Type.ErrorUnion",
476 @"Type.ErrorSet",
477 @"Type.Enum",
478 @"Type.Enum.Mode",
479 @"Type.Union",
480 @"Type.Union.FieldAttributes",
481 @"Type.Struct",
482 @"Type.Struct.FieldAttributes",
483 @"Type.ContainerLayout",
484 @"Type.Opaque",
485 @"Type.Spirv",
486 @"Type.Spirv.Image",
487 @"Type.Spirv.Image.Usage",
488 @"Type.Spirv.Image.Format",
489 @"Type.Spirv.Image.Dimensionality",
490 @"Type.Spirv.Image.Depth",
491 @"Type.Spirv.Image.Access",
492
493 panic,
494 @"panic.call",
495 @"panic.sentinelMismatch",
496 @"panic.unwrapError",
497 @"panic.outOfBounds",
498 @"panic.startGreaterThanEnd",
499 @"panic.inactiveUnionField",
500 @"panic.sliceCastLenRemainder",
501 @"panic.reachedUnreachable",
502 @"panic.unwrapNull",
503 @"panic.castToNull",
504 @"panic.incorrectAlignment",
505 @"panic.invalidErrorCode",
506 @"panic.unexpectedErrorCode",
507 @"panic.integerOutOfBounds",
508 @"panic.integerOverflow",
509 @"panic.shlOverflow",
510 @"panic.shrOverflow",
511 @"panic.divideByZero",
512 @"panic.exactDivisionRemainder",
513 @"panic.integerPartOutOfBounds",
514 @"panic.corruptSwitch",
515 @"panic.shiftRhsTooBig",
516 @"panic.invalidEnumValue",
517 @"panic.forLenMismatch",
518 @"panic.copyLenMismatch",
519 @"panic.memcpyAlias",
520 @"panic.noreturnReturned",
521 @"panic.loadUninstantiableType",
522
523 VaList,
524
525 assembly,
526 @"assembly.Clobbers",
527
528 /// Determines what kind of validation will be done to the decl's value.
529 pub fn kind(decl: StdLangDecl) enum { type, func, string } {
530 return switch (decl) {
531 .returnError => .func,
532
533 .StackTrace,
534 .CallingConvention,
535 .SourceLocation,
536 .Signedness,
537 .AddressSpace,
538 .VaList,
539 .CallModifier,
540 .AtomicOrder,
541 .AtomicRmwOp,
542 .ReduceOp,
543 .FloatMode,
544 .PrefetchOptions,
545 .ExportOptions,
546 .ExternOptions,
547 .BranchHint,
548 .assembly,
549 .@"assembly.Clobbers",
550 => .type,
551
552 .Type,
553 .@"Type.Fn",
554 .@"Type.Fn.ParamAttributes",
555 .@"Type.Fn.Attributes",
556 .@"Type.Int",
557 .@"Type.Float",
558 .@"Type.Pointer",
559 .@"Type.Pointer.Size",
560 .@"Type.Pointer.Attributes",
561 .@"Type.Array",
562 .@"Type.Vector",
563 .@"Type.Optional",
564 .@"Type.ErrorUnion",
565 .@"Type.ErrorSet",
566 .@"Type.Enum",
567 .@"Type.Enum.Mode",
568 .@"Type.Union",
569 .@"Type.Union.FieldAttributes",
570 .@"Type.Struct",
571 .@"Type.Struct.FieldAttributes",
572 .@"Type.ContainerLayout",
573 .@"Type.Opaque",
574 .@"Type.Spirv",
575 .@"Type.Spirv.Image",
576 .@"Type.Spirv.Image.Usage",
577 .@"Type.Spirv.Image.Format",
578 .@"Type.Spirv.Image.Dimensionality",
579 .@"Type.Spirv.Image.Depth",
580 .@"Type.Spirv.Image.Access",
581 => .type,
582
583 .panic => .type,
584
585 .@"panic.call",
586 .@"panic.sentinelMismatch",
587 .@"panic.unwrapError",
588 .@"panic.outOfBounds",
589 .@"panic.startGreaterThanEnd",
590 .@"panic.inactiveUnionField",
591 .@"panic.sliceCastLenRemainder",
592 .@"panic.reachedUnreachable",
593 .@"panic.unwrapNull",
594 .@"panic.castToNull",
595 .@"panic.incorrectAlignment",
596 .@"panic.invalidErrorCode",
597 .@"panic.unexpectedErrorCode",
598 .@"panic.integerOutOfBounds",
599 .@"panic.integerOverflow",
600 .@"panic.shlOverflow",
601 .@"panic.shrOverflow",
602 .@"panic.divideByZero",
603 .@"panic.exactDivisionRemainder",
604 .@"panic.integerPartOutOfBounds",
605 .@"panic.corruptSwitch",
606 .@"panic.shiftRhsTooBig",
607 .@"panic.invalidEnumValue",
608 .@"panic.forLenMismatch",
609 .@"panic.copyLenMismatch",
610 .@"panic.memcpyAlias",
611 .@"panic.noreturnReturned",
612 .@"panic.loadUninstantiableType",
613 => .func,
614 };
615 }
616
617 /// Resolution of these values is done in three distinct stages:
618 /// * Resolution of `std.lang.Panic` and everything under it
619 /// * Resolution of `VaList`
620 /// * Resolution of `assembly`
621 /// * Everything else
622 ///
623 /// Panics are separated because they are provided by the user, so must be able to use
624 /// things like reification.
625 ///
626 /// `VaList` is separate because its value depends on the target, so it needs some reflection
627 /// machinery to work; additionally, it is `@compileError` on some targets, so must be referenced
628 /// by itself.
629 ///
630 /// `assembly` is separate because its value depends on the target.
631 pub fn stage(decl: StdLangDecl) InternPool.MemoizedStateStage {
632 return switch (decl) {
633 .VaList => .va_list,
634 .assembly, .@"assembly.Clobbers" => .assembly,
635 else => {
636 if (@backingInt(decl) <= @backingInt(StdLangDecl.@"Type.Spirv.Image.Access")) {
637 return .main;
638 } else {
639 return .panic;
640 }
641 },
642 };
643 }
644
645 /// Based on the tag name, determines how to access this decl; either as a direct child of the
646 /// `std.lang` namespace, or as a child of some preceding `StdLangDecl` value.
647 pub fn access(decl: StdLangDecl) union(enum) {
648 direct: []const u8,
649 nested: struct { StdLangDecl, []const u8 },
650 } {
651 @setEvalBranchQuota(2000);
652 return switch (decl) {
653 inline else => |tag| {
654 const name = @tagName(tag);
655 const split = (comptime std.mem.findScalarLast(u8, name, '.')) orelse return .{ .direct = name };
656 const parent = @field(StdLangDecl, name[0..split]);
657 comptime assert(@backingInt(parent) < @backingInt(tag)); // dependencies ordered correctly
658 return .{ .nested = .{ parent, name[split + 1 ..] } };
659 },
660 };
661 }
662
663 const Memoized = std.enums.EnumArray(StdLangDecl, InternPool.Index);
664};
665
666pub const SimplePanicId = enum {
667 reached_unreachable,
668 unwrap_null,
669 cast_to_null,
670 incorrect_alignment,
671 invalid_error_code,
672 integer_out_of_bounds,
673 integer_overflow,
674 shl_overflow,
675 shr_overflow,
676 divide_by_zero,
677 exact_division_remainder,
678 integer_part_out_of_bounds,
679 corrupt_switch,
680 shift_rhs_too_big,
681 invalid_enum_value,
682 for_len_mismatch,
683 copy_len_mismatch,
684 memcpy_alias,
685 noreturn_returned,
686 load_uninstantiable_type,
687
688 pub fn toStdLangDecl(id: SimplePanicId) StdLangDecl {
689 return switch (id) {
690 // zig fmt: off
691 .reached_unreachable => .@"panic.reachedUnreachable",
692 .unwrap_null => .@"panic.unwrapNull",
693 .cast_to_null => .@"panic.castToNull",
694 .incorrect_alignment => .@"panic.incorrectAlignment",
695 .invalid_error_code => .@"panic.invalidErrorCode",
696 .integer_out_of_bounds => .@"panic.integerOutOfBounds",
697 .integer_overflow => .@"panic.integerOverflow",
698 .shl_overflow => .@"panic.shlOverflow",
699 .shr_overflow => .@"panic.shrOverflow",
700 .divide_by_zero => .@"panic.divideByZero",
701 .exact_division_remainder => .@"panic.exactDivisionRemainder",
702 .integer_part_out_of_bounds => .@"panic.integerPartOutOfBounds",
703 .corrupt_switch => .@"panic.corruptSwitch",
704 .shift_rhs_too_big => .@"panic.shiftRhsTooBig",
705 .invalid_enum_value => .@"panic.invalidEnumValue",
706 .for_len_mismatch => .@"panic.forLenMismatch",
707 .copy_len_mismatch => .@"panic.copyLenMismatch",
708 .memcpy_alias => .@"panic.memcpyAlias",
709 .noreturn_returned => .@"panic.noreturnReturned",
710 .load_uninstantiable_type => .@"panic.loadUninstantiableType",
711 // zig fmt: on
712 };
713 }
714};
715
716pub const GlobalErrorSet = std.array_hash_map.Auto(InternPool.NullTerminatedString, void);
717
718pub const CImportError = struct {
719 offset: u32,
720 line: u32,
721 column: u32,
722 path: ?[*:0]u8,
723 source_line: ?[*:0]u8,
724 msg: [*:0]u8,
725
726 pub fn deinit(err: CImportError, gpa: Allocator) void {
727 if (err.path) |some| gpa.free(std.mem.span(some));
728 if (err.source_line) |some| gpa.free(std.mem.span(some));
729 gpa.free(std.mem.span(err.msg));
730 }
731};
732
733pub const ErrorInt = u32;
734
735pub const Exported = union(enum) {
736 /// The Nav being exported. Note this is *not* the Nav corresponding to the AnalUnit performing the export.
737 nav: InternPool.Nav.Index,
738 /// Constant value being exported.
739 uav: InternPool.Index,
740
741 pub fn getValue(exported: Exported, zcu: *Zcu) Value {
742 return switch (exported) {
743 .nav => |nav| zcu.navValue(nav),
744 .uav => |uav| Value.fromInterned(uav),
745 };
746 }
747
748 pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment {
749 return switch (exported) {
750 .nav => |nav| zcu.intern_pool.getNav(nav).resolved.?.@"align",
751 .uav => .none,
752 };
753 }
754};
755
756pub const Export = struct {
757 opts: Options,
758 src: LazySrcLoc,
759 exported: Exported,
760
761 pub const Options = struct {
762 name: InternPool.NullTerminatedString,
763 linkage: std.lang.GlobalLinkage = .strong,
764 section: InternPool.OptionalNullTerminatedString = .none,
765 visibility: std.lang.SymbolVisibility = .default,
766 };
767
768 /// Index into `all_exports`.
769 pub const Index = enum(u32) {
770 _,
771
772 pub fn ptr(i: Index, zcu: *const Zcu) *Export {
773 return &zcu.all_exports.items[@backingInt(i)];
774 }
775 };
776};
777
778pub const CompileLogLine = struct {
779 next: Index.Optional,
780 /// Does *not* include the trailing newline.
781 data: InternPool.NullTerminatedString,
782 pub const Index = enum(u32) {
783 _,
784 pub fn get(idx: Index, zcu: *Zcu) *CompileLogLine {
785 return &zcu.compile_log_lines.items[@backingInt(idx)];
786 }
787 pub fn toOptional(idx: Index) Optional {
788 return @fromBackingInt(@intCast(@backingInt(idx)));
789 }
790 pub const Optional = enum(u32) {
791 none = std.math.maxInt(u32),
792 _,
793 pub fn unwrap(opt: Optional) ?Index {
794 return switch (opt) {
795 .none => null,
796 _ => @fromBackingInt(@intCast(@backingInt(opt))),
797 };
798 }
799 };
800 };
801};
802
803pub const Reference = struct {
804 /// The `AnalUnit` whose semantic analysis was triggered by this reference.
805 referenced: AnalUnit,
806 /// Index into `all_references` of the next `Reference` triggered by the same `AnalUnit`.
807 /// `std.math.maxInt(u32)` is the sentinel.
808 next: u32,
809 /// The source location of the reference.
810 src: LazySrcLoc,
811 /// If not `.none`, this is the index of the `InlineReferenceFrame` which should appear
812 /// between the referencer and `referenced` in the reference trace. These frames represent
813 /// inline calls, which do not create actual references (since they happen in the caller's
814 /// `AnalUnit`), but do show in the reference trace.
815 inline_frame: InlineReferenceFrame.Index.Optional,
816};
817
818pub const InlineReferenceFrame = struct {
819 /// The inline *callee*; that is, the function which was called inline.
820 /// The *caller* is either `parent`, or else the unit causing the original `Reference`.
821 callee: InternPool.Index,
822 /// The source location of the inline call, in the *caller*.
823 call_src: LazySrcLoc,
824 /// If not `.none`, a frame which should appear directly below this one.
825 /// This will be the "parent" inline call; this frame's `callee` is our caller.
826 parent: InlineReferenceFrame.Index.Optional,
827
828 pub const Index = enum(u32) {
829 _,
830 pub fn ptr(idx: Index, zcu: *Zcu) *InlineReferenceFrame {
831 return &zcu.inline_reference_frames.items[@backingInt(idx)];
832 }
833 pub fn toOptional(idx: Index) Optional {
834 return @fromBackingInt(@intCast(@backingInt(idx)));
835 }
836 pub const Optional = enum(u32) {
837 none = std.math.maxInt(u32),
838 _,
839 pub fn unwrap(opt: Optional) ?Index {
840 return switch (opt) {
841 .none => null,
842 _ => @fromBackingInt(@intCast(@backingInt(opt))),
843 };
844 }
845 };
846 };
847};
848
849pub const TypeReference = struct {
850 /// The container type which was referenced.
851 referenced: InternPool.Index,
852 /// Index into `all_type_references` of the next `TypeReference` triggered by the same `AnalUnit`.
853 /// `std.math.maxInt(u32)` is the sentinel.
854 next: u32,
855 /// The source location of the reference.
856 src: LazySrcLoc,
857};
858
859/// The container that structs, enums, unions, and opaques have.
860pub const Namespace = struct {
861 parent: OptionalIndex,
862 file_scope: File.Index,
863 generation: u32,
864 /// Will be a struct, enum, union, or opaque.
865 owner_type: InternPool.Index,
866 /// Members of the namespace which are marked `pub`.
867 pub_decls: std.array_hash_map.Custom(InternPool.Nav.Index, void, NavNameContext, true) = .empty,
868 /// Members of the namespace which are *not* marked `pub`.
869 priv_decls: std.array_hash_map.Custom(InternPool.Nav.Index, void, NavNameContext, true) = .empty,
870 /// All `comptime` declarations in this namespace. We store these purely so that incremental
871 /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.
872 comptime_decls: std.ArrayList(InternPool.ComptimeUnit.Id) = .empty,
873 /// All `test` declarations in this namespace. We store these purely so that incremental
874 /// compilation can re-use the existing `Nav`s when a namespace changes.
875 test_decls: std.ArrayList(InternPool.Nav.Index) = .empty,
876
877 pub const Index = InternPool.NamespaceIndex;
878 pub const OptionalIndex = InternPool.OptionalNamespaceIndex;
879
880 const NavNameContext = struct {
881 zcu: *Zcu,
882
883 pub fn hash(ctx: NavNameContext, nav: InternPool.Nav.Index) u32 {
884 const name = ctx.zcu.intern_pool.getNav(nav).name;
885 return std.hash.int(@backingInt(name));
886 }
887
888 pub fn eql(ctx: NavNameContext, a_nav: InternPool.Nav.Index, b_nav: InternPool.Nav.Index, b_index: usize) bool {
889 _ = b_index;
890 const a_name = ctx.zcu.intern_pool.getNav(a_nav).name;
891 const b_name = ctx.zcu.intern_pool.getNav(b_nav).name;
892 return a_name == b_name;
893 }
894 };
895
896 pub const NameAdapter = struct {
897 zcu: *Zcu,
898
899 pub fn hash(ctx: NameAdapter, s: InternPool.NullTerminatedString) u32 {
900 _ = ctx;
901 return std.hash.int(@backingInt(s));
902 }
903
904 pub fn eql(ctx: NameAdapter, a: InternPool.NullTerminatedString, b_nav: InternPool.Nav.Index, b_index: usize) bool {
905 _ = b_index;
906 return a == ctx.zcu.intern_pool.getNav(b_nav).name;
907 }
908 };
909
910 pub fn fileScope(ns: Namespace, zcu: *Zcu) *File {
911 return zcu.fileByIndex(ns.file_scope);
912 }
913
914 pub fn fileScopeIp(ns: Namespace, ip: *InternPool) *File {
915 return ip.filePtr(ns.file_scope);
916 }
917
918 /// This renders e.g. "std/fs.zig:Dir.OpenOptions"
919 pub fn renderFullyQualifiedDebugName(
920 ns: Namespace,
921 zcu: *Zcu,
922 name: InternPool.NullTerminatedString,
923 writer: *Writer,
924 ) @TypeOf(writer).Error!void {
925 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: {
926 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName(
927 zcu,
928 zcu.declPtr(ns.decl_index).name,
929 writer,
930 );
931 break :sep '.';
932 } else sep: {
933 try ns.fileScope(zcu).renderFullyQualifiedDebugName(writer);
934 break :sep ':';
935 };
936 if (name != .empty) try writer.print("{c}{f}", .{ sep, name.fmt(&zcu.intern_pool) });
937 }
938
939 pub fn internFullyQualifiedName(
940 ns: Namespace,
941 ip: *InternPool,
942 gpa: Allocator,
943 io: Io,
944 tid: Zcu.PerThread.Id,
945 name: InternPool.NullTerminatedString,
946 ) !InternPool.NullTerminatedString {
947 const ns_name = Type.fromInterned(ns.owner_type).containerTypeName(ip);
948 if (name == .empty) return ns_name;
949 return ip.getOrPutStringFmt(gpa, io, tid, "{f}.{f}", .{ ns_name.fmt(ip), name.fmt(ip) }, .no_embedded_nulls);
950 }
951};
952
953pub const File = struct {
954 status: enum {
955 /// We have not yet attempted to load this file.
956 /// `stat` is not populated and may be `undefined`.
957 never_loaded,
958 /// A filesystem access failed. It should be retried on the next update.
959 /// There is guaranteed to be a `failed_files` entry with at least one message.
960 /// ZIR/ZOIR errors should not be emitted as `zir`/`zoir` is not up-to-date.
961 /// `stat` is not populated and may be `undefined`.
962 retryable_failure,
963 /// This file has failed parsing, AstGen, or ZonGen.
964 /// There is guaranteed to be a `failed_files` entry, which may or may not have messages.
965 /// ZIR/ZOIR errors *should* be emitted as `zir`/`zoir` is up-to-date.
966 /// `stat` is populated.
967 astgen_failure,
968 /// Parsing and AstGen/ZonGen of this file has succeeded.
969 /// There may still be a `failed_files` entry, e.g. for non-fatal AstGen errors.
970 /// `stat` is populated.
971 success,
972 },
973 /// Whether this is populated depends on `status`.
974 stat: Cache.File.Stat,
975
976 /// Whether this file is the generated file of a "builtin" module. This matters because those
977 /// files are generated and stored in-nemory rather than being read off-disk. The rest of the
978 /// pipeline generally shouldn't care about this.
979 is_builtin: bool,
980
981 /// The path of this file. It is important that this path has a "canonical form" because files
982 /// are deduplicated based on path; `Compilation.Path` guarantees this. Owned by this `File`,
983 /// allocated into `gpa`.
984 path: Compilation.Path,
985
986 /// Populated only when emitting error messages; see `getSource`.
987 source: ?[:0]const u8,
988 /// Populated only when emitting error messages; see `getTree`.
989 tree: ?Ast,
990
991 zir: ?Zir,
992 zoir: ?Zoir,
993
994 /// Module that this file is a part of, managed externally.
995 /// This is initially `null`. After AstGen, a pass is run to determine which module each
996 /// file belongs to, at which point this field is set. It is never set to `null` again;
997 /// this is so that if the file starts belonging to a different module instead, we can
998 /// tell, and invalidate dependencies as needed (see `module_changed`).
999 /// During semantic analysis, this is always non-`null` for alive files (i.e. those which
1000 /// have imports targeting them).
1001 mod: ?*Module,
1002 /// Relative to the root directory of `mod`. If `mod == null`, this field is `undefined`.
1003 /// This memory is managed externally and must not be directly freed.
1004 /// Its lifetime is at least equal to that of this `File`.
1005 sub_file_path: []const u8,
1006
1007 /// If this file's module identity changes on an incremental update, this flag is set to signal
1008 /// to `Zcu.updateZirRefs` that all references to this file must be invalidated. This matters
1009 /// because changing your module changes things like your optimization mode and codegen flags,
1010 /// so everything needs to be re-done. `updateZirRefs` is responsible for resetting this flag.
1011 module_changed: bool,
1012
1013 /// The ZIR for this file from the last update with no file failures. As such, this ZIR is never
1014 /// failed (although it may have compile errors).
1015 ///
1016 /// Because updates with file failures do not perform ZIR mapping or semantic analysis, we keep
1017 /// this around so we have the "old" ZIR to map when an update is ready to do so. Once such an
1018 /// update occurs, this field is unloaded, since it is no longer necessary.
1019 ///
1020 /// In other words, if `TrackedInst`s are tied to ZIR other than what's in the `zir` field, this
1021 /// field is populated with that old ZIR.
1022 prev_zir: ?*Zir,
1023
1024 /// This field serves a similar purpose to `prev_zir`, but for ZOIR. However, since we do not
1025 /// need to map old ZOIR to new ZOIR -- instead only invalidating dependencies if the ZOIR
1026 /// changed -- this field is just a simple boolean.
1027 ///
1028 /// When `zoir` is updated, this field is set to `true`. In `updateZirRefs`, if this is `true`,
1029 /// we invalidate the corresponding `source_file` dependency, and reset it to `false`.
1030 zoir_invalidated: bool,
1031
1032 pub const Path = struct {
1033 root: enum {
1034 cwd,
1035 fs_root,
1036 local_cache,
1037 global_cache,
1038 lib_dir,
1039 },
1040 };
1041
1042 /// A single reference to a file.
1043 pub const Reference = union(enum) {
1044 analysis_root: *Module,
1045 import: struct {
1046 importer: Zcu.File.Index,
1047 tok: Ast.TokenIndex,
1048 /// If the file is imported as the root of a module, this is that module.
1049 /// `null` means the file was imported directly by path.
1050 module: ?*Module,
1051 },
1052 };
1053
1054 pub fn getMode(self: File) Ast.Mode {
1055 // We never create a `File` whose path doesn't give a mode.
1056 return modeFromPath(self.path.sub_path).?;
1057 }
1058
1059 pub fn modeFromPath(path: []const u8) ?Ast.Mode {
1060 if (std.mem.endsWith(u8, path, ".zon")) {
1061 return .zon;
1062 } else if (std.mem.endsWith(u8, path, ".zig")) {
1063 return .zig;
1064 } else {
1065 return null;
1066 }
1067 }
1068
1069 pub fn unload(file: *File, gpa: Allocator) void {
1070 if (file.zoir) |zoir| zoir.deinit(gpa);
1071 file.unloadTree(gpa);
1072 file.unloadSource(gpa);
1073 file.unloadZir(gpa);
1074 }
1075
1076 pub fn unloadTree(file: *File, gpa: Allocator) void {
1077 if (file.tree) |*tree| {
1078 tree.deinit(gpa);
1079 file.tree = null;
1080 }
1081 }
1082
1083 pub fn unloadSource(file: *File, gpa: Allocator) void {
1084 if (file.source) |source| {
1085 gpa.free(source);
1086 file.source = null;
1087 }
1088 }
1089
1090 pub fn unloadZir(file: *File, gpa: Allocator) void {
1091 if (file.zir) |*zir| {
1092 zir.deinit(gpa);
1093 file.zir = null;
1094 }
1095 }
1096
1097 pub const GetSourceError = error{
1098 OutOfMemory,
1099 FileChanged,
1100 } || std.Io.File.OpenError || std.Io.File.Reader.Error;
1101
1102 /// This must only be called in error conditions where `stat` *is* populated. It returns the
1103 /// contents of the source file, assuming the stat has not changed since it was originally
1104 /// loaded.
1105 pub fn getSource(file: *File, zcu: *const Zcu) GetSourceError![:0]const u8 {
1106 const gpa = zcu.gpa;
1107 const io = zcu.comp.io;
1108
1109 if (file.source) |source| return source;
1110
1111 switch (file.status) {
1112 .never_loaded => unreachable, // stat must be populated
1113 .retryable_failure => unreachable, // stat must be populated
1114 .astgen_failure, .success => {},
1115 }
1116
1117 assert(file.stat.size <= std.math.maxInt(u32)); // `PerThread.updateFile` checks this
1118
1119 var f = f: {
1120 const dir, const sub_path = file.path.openInfo(zcu.comp.dirs);
1121 break :f try dir.openFile(io, sub_path, .{});
1122 };
1123 defer f.close(io);
1124
1125 const stat = f.stat(io) catch |err| switch (err) {
1126 error.Streaming => {
1127 // Since `file.stat` is populated, this was previously a file stream; since it is
1128 // now not a file stream, it must have changed.
1129 return error.FileChanged;
1130 },
1131 else => |e| return e,
1132 };
1133
1134 if (stat.inode != file.stat.inode or
1135 stat.size != file.stat.size or
1136 stat.mtime.nanoseconds != file.stat.mtime.nanoseconds)
1137 {
1138 return error.FileChanged;
1139 }
1140
1141 const source = try gpa.allocSentinel(u8, @intCast(file.stat.size), 0);
1142 errdefer gpa.free(source);
1143
1144 var file_reader = f.reader(io, &.{});
1145 file_reader.size = stat.size;
1146 file_reader.interface.readSliceAll(source) catch return file_reader.err.?;
1147
1148 file.source = source;
1149 errdefer comptime unreachable; // don't error after populating `source`
1150
1151 return source;
1152 }
1153
1154 /// This must only be called in error conditions where `stat` *is* populated. It returns the
1155 /// parsed AST of the source file, assuming the stat has not changed since it was originally
1156 /// loaded.
1157 pub fn getTree(file: *File, zcu: *const Zcu) GetSourceError!*const Ast {
1158 if (file.tree) |*tree| return tree;
1159
1160 const source = try file.getSource(zcu);
1161 file.tree = try .parse(zcu.gpa, source, .{ .mode = file.getMode() });
1162 return &file.tree.?;
1163 }
1164
1165 pub fn fullyQualifiedNameLen(file: File) usize {
1166 const ext = std.fs.path.extension(file.sub_file_path);
1167 return file.sub_file_path.len - ext.len;
1168 }
1169
1170 pub fn renderFullyQualifiedName(file: File, writer: *Writer) !void {
1171 // Convert all the slashes into dots and truncate the extension.
1172 const ext = std.fs.path.extension(file.sub_file_path);
1173 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
1174 for (noext) |byte| switch (byte) {
1175 '/', '\\' => try writer.writeByte('.'),
1176 else => try writer.writeByte(byte),
1177 };
1178 }
1179
1180 pub fn renderFullyQualifiedDebugName(file: File, writer: *Writer) !void {
1181 for (file.sub_file_path) |byte| switch (byte) {
1182 '/', '\\' => try writer.writeByte('/'),
1183 else => try writer.writeByte(byte),
1184 };
1185 }
1186
1187 pub fn internFullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
1188 const ip = &pt.zcu.intern_pool;
1189 const comp = pt.zcu.comp;
1190 const gpa = comp.gpa;
1191 const io = comp.io;
1192 const string_bytes = ip.getLocal(pt.tid).getMutableStringBytes(gpa, io);
1193 var w: Writer = .fixed((try string_bytes.addManyAsSlice(file.fullyQualifiedNameLen()))[0]);
1194 file.renderFullyQualifiedName(&w) catch unreachable;
1195 assert(w.end == w.buffer.len);
1196 return ip.getOrPutTrailingString(gpa, io, pt.tid, @intCast(w.end), .no_embedded_nulls);
1197 }
1198
1199 pub const Index = InternPool.FileIndex;
1200
1201 pub fn errorBundleWholeFileSrc(
1202 file: *File,
1203 zcu: *const Zcu,
1204 eb: *std.zig.ErrorBundle.Wip,
1205 ) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex {
1206 return eb.addSourceLocation(.{
1207 .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}),
1208 .span_start = 0,
1209 .span_main = 0,
1210 .span_end = 0,
1211 .line = 0,
1212 .column = 0,
1213 .source_line = 0,
1214 });
1215 }
1216 /// Asserts that the tree has already been loaded with `getTree`.
1217 pub fn errorBundleTokenSrc(
1218 file: *File,
1219 tok: Ast.TokenIndex,
1220 zcu: *const Zcu,
1221 eb: *std.zig.ErrorBundle.Wip,
1222 ) Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex {
1223 const tree = &file.tree.?;
1224 const start = tree.tokenStart(tok);
1225 const end = start + tree.tokenSlice(tok).len;
1226 const loc = std.zig.findLineColumn(file.source.?, start);
1227 return eb.addSourceLocation(.{
1228 .src_path = try eb.printString("{f}", .{file.path.fmt(zcu.comp)}),
1229 .span_start = start,
1230 .span_main = start,
1231 .span_end = @intCast(end),
1232 .line = @intCast(loc.line),
1233 .column = @intCast(loc.column),
1234 .source_line = try eb.addString(loc.source_line),
1235 });
1236 }
1237};
1238
1239/// Represents the contents of a file loaded with `@embedFile`.
1240pub const EmbedFile = struct {
1241 path: Compilation.Path,
1242 /// `.none` means the file was not loaded, so `stat` is undefined.
1243 val: InternPool.Index,
1244 /// If this is `null` and `val` is `.none`, the file has never been loaded.
1245 err: ?(Io.File.OpenError || Io.File.StatError || Io.File.Reader.Error || error{UnexpectedEof}),
1246 stat: Cache.File.Stat,
1247
1248 pub const Index = enum(u32) {
1249 _,
1250 pub fn get(idx: Index, zcu: *const Zcu) *EmbedFile {
1251 return zcu.embed_table.keys()[@backingInt(idx)];
1252 }
1253 };
1254};
1255
1256/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
1257/// Its memory is managed with the general purpose allocator so that they
1258/// can be created and destroyed in response to incremental updates.
1259pub const ErrorMsg = struct {
1260 src_loc: LazySrcLoc,
1261 msg: []const u8,
1262 notes: []ErrorMsg = &.{},
1263 reference_trace_root: AnalUnit.Optional = .none,
1264
1265 pub fn order(lhs: *const ErrorMsg, rhs: *const ErrorMsg, zcu: *Zcu) std.math.Order {
1266 return lhs.src_loc.order(rhs.src_loc, zcu).differ() orelse
1267 std.mem.order(u8, lhs.msg, rhs.msg).differ() orelse
1268 std.math.order(lhs.notes.len, rhs.notes.len).differ() orelse
1269 for (lhs.notes, rhs.notes) |*lhs_note, *rhs_note| {
1270 if (order(lhs_note, rhs_note, zcu).differ()) |o| break o;
1271 } else .eq;
1272 }
1273
1274 pub fn create(
1275 gpa: Allocator,
1276 src_loc: LazySrcLoc,
1277 comptime format: []const u8,
1278 args: anytype,
1279 ) !*ErrorMsg {
1280 assert(src_loc.offset != .unneeded);
1281 const err_msg = try gpa.create(ErrorMsg);
1282 errdefer gpa.destroy(err_msg);
1283 err_msg.* = try ErrorMsg.init(gpa, src_loc, format, args);
1284 return err_msg;
1285 }
1286
1287 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`,
1288 /// as well as all notes.
1289 pub fn destroy(err_msg: *ErrorMsg, gpa: Allocator) void {
1290 err_msg.deinit(gpa);
1291 gpa.destroy(err_msg);
1292 }
1293
1294 pub fn init(gpa: Allocator, src_loc: LazySrcLoc, comptime format: []const u8, args: anytype) !ErrorMsg {
1295 return .{
1296 .src_loc = src_loc,
1297 .msg = try std.fmt.allocPrint(gpa, format, args),
1298 };
1299 }
1300
1301 pub fn deinit(err_msg: *ErrorMsg, gpa: Allocator) void {
1302 for (err_msg.notes) |*note| {
1303 note.deinit(gpa);
1304 }
1305 gpa.free(err_msg.notes);
1306 gpa.free(err_msg.msg);
1307 err_msg.* = undefined;
1308 }
1309};
1310
1311pub const AstGenSrc = union(enum) {
1312 root,
1313 import: struct {
1314 importing_file: Zcu.File.Index,
1315 import_tok: std.zig.Ast.TokenIndex,
1316 },
1317};
1318
1319/// Canonical reference to a position within a source file.
1320pub const SrcLoc = struct {
1321 file_scope: *File,
1322 base_node: Ast.Node.Index,
1323 /// Relative to `base_node`.
1324 lazy: LazySrcLoc.Offset,
1325
1326 pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex {
1327 const tree = src_loc.file_scope.tree.?;
1328 return tree.firstToken(src_loc.base_node);
1329 }
1330
1331 pub const Span = Ast.Span;
1332
1333 pub fn span(src_loc: SrcLoc, zcu: *const Zcu) !Span {
1334 switch (src_loc.lazy) {
1335 .unneeded => unreachable,
1336
1337 .byte_abs => |byte_index| return Span{ .start = byte_index, .end = byte_index + 1, .main = byte_index },
1338
1339 .token_abs => |tok_index| {
1340 const tree = try src_loc.file_scope.getTree(zcu);
1341 const start = tree.tokenStart(tok_index);
1342 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1343 return Span{ .start = start, .end = end, .main = start };
1344 },
1345 .node_abs => |node| {
1346 const tree = try src_loc.file_scope.getTree(zcu);
1347 return tree.nodeToSpan(node);
1348 },
1349 .byte_offset => |byte_off| {
1350 const tree = try src_loc.file_scope.getTree(zcu);
1351 const tok_index = src_loc.baseSrcToken();
1352 const start = tree.tokenStart(tok_index) + byte_off;
1353 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1354 return Span{ .start = start, .end = end, .main = start };
1355 },
1356 .token_offset => |tok_off| {
1357 const tree = try src_loc.file_scope.getTree(zcu);
1358 const tok_index = tok_off.toAbsolute(src_loc.baseSrcToken());
1359 const start = tree.tokenStart(tok_index);
1360 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1361 return Span{ .start = start, .end = end, .main = start };
1362 },
1363 .node_offset => |traced_off| {
1364 const node_off = traced_off.x;
1365 const tree = try src_loc.file_scope.getTree(zcu);
1366 const node = node_off.toAbsolute(src_loc.base_node);
1367 return tree.nodeToSpan(node);
1368 },
1369 .node_offset_main_token => |node_off| {
1370 const tree = try src_loc.file_scope.getTree(zcu);
1371 const node = node_off.toAbsolute(src_loc.base_node);
1372 const main_token = tree.nodeMainToken(node);
1373 return tree.tokensToSpan(main_token, main_token, main_token);
1374 },
1375 .node_offset_bin_op => |node_off| {
1376 const tree = try src_loc.file_scope.getTree(zcu);
1377 const node = node_off.toAbsolute(src_loc.base_node);
1378 return tree.nodeToSpan(node);
1379 },
1380 .node_offset_initializer => |node_off| {
1381 const tree = try src_loc.file_scope.getTree(zcu);
1382 const node = node_off.toAbsolute(src_loc.base_node);
1383 return tree.tokensToSpan(
1384 tree.firstToken(node) - 3,
1385 tree.lastToken(node),
1386 tree.nodeMainToken(node) - 2,
1387 );
1388 },
1389 .node_offset_var_decl_ty => |node_off| {
1390 const tree = try src_loc.file_scope.getTree(zcu);
1391 const node = node_off.toAbsolute(src_loc.base_node);
1392 const full = switch (tree.nodeTag(node)) {
1393 .global_var_decl,
1394 .local_var_decl,
1395 .simple_var_decl,
1396 .aligned_var_decl,
1397 => tree.fullVarDecl(node).?,
1398 else => unreachable,
1399 };
1400 if (full.ast.type_node.unwrap()) |type_node| {
1401 return tree.nodeToSpan(type_node);
1402 }
1403 const tok_index = full.ast.mut_token + 1; // the name token
1404 const start = tree.tokenStart(tok_index);
1405 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1406 return Span{ .start = start, .end = end, .main = start };
1407 },
1408 .node_offset_var_decl_align => |node_off| {
1409 const tree = try src_loc.file_scope.getTree(zcu);
1410 const node = node_off.toAbsolute(src_loc.base_node);
1411 var buf: [1]Ast.Node.Index = undefined;
1412 const align_node = if (tree.fullVarDecl(node)) |v|
1413 v.ast.align_node.unwrap().?
1414 else if (tree.fullFnProto(&buf, node)) |f|
1415 f.ast.align_expr.unwrap().?
1416 else
1417 unreachable;
1418 return tree.nodeToSpan(align_node);
1419 },
1420 .node_offset_var_decl_section => |node_off| {
1421 const tree = try src_loc.file_scope.getTree(zcu);
1422 const node = node_off.toAbsolute(src_loc.base_node);
1423 var buf: [1]Ast.Node.Index = undefined;
1424 const section_node = if (tree.fullVarDecl(node)) |v|
1425 v.ast.section_node.unwrap().?
1426 else if (tree.fullFnProto(&buf, node)) |f|
1427 f.ast.section_expr.unwrap().?
1428 else
1429 unreachable;
1430 return tree.nodeToSpan(section_node);
1431 },
1432 .node_offset_var_decl_addrspace => |node_off| {
1433 const tree = try src_loc.file_scope.getTree(zcu);
1434 const node = node_off.toAbsolute(src_loc.base_node);
1435 var buf: [1]Ast.Node.Index = undefined;
1436 const addrspace_node = if (tree.fullVarDecl(node)) |v|
1437 v.ast.addrspace_node.unwrap().?
1438 else if (tree.fullFnProto(&buf, node)) |f|
1439 f.ast.addrspace_expr.unwrap().?
1440 else
1441 unreachable;
1442 return tree.nodeToSpan(addrspace_node);
1443 },
1444 .node_offset_var_decl_init => |node_off| {
1445 const tree = try src_loc.file_scope.getTree(zcu);
1446 const node = node_off.toAbsolute(src_loc.base_node);
1447 const init_node = switch (tree.nodeTag(node)) {
1448 .global_var_decl,
1449 .local_var_decl,
1450 .aligned_var_decl,
1451 .simple_var_decl,
1452 => tree.fullVarDecl(node).?.ast.init_node.unwrap().?,
1453 .assign_destructure => tree.assignDestructure(node).ast.value_expr,
1454 else => unreachable,
1455 };
1456 return tree.nodeToSpan(init_node);
1457 },
1458 .node_offset_builtin_call_arg => |builtin_arg| {
1459 const tree = try src_loc.file_scope.getTree(zcu);
1460 const node = builtin_arg.builtin_call_node.toAbsolute(src_loc.base_node);
1461 var buf: [2]Ast.Node.Index = undefined;
1462 const params = tree.builtinCallParams(&buf, node).?;
1463 return tree.nodeToSpan(params[builtin_arg.arg_index]);
1464 },
1465 .node_offset_ptrcast_operand => |node_off| {
1466 const tree = try src_loc.file_scope.getTree(zcu);
1467
1468 var node = node_off.toAbsolute(src_loc.base_node);
1469 while (true) {
1470 switch (tree.nodeTag(node)) {
1471 .builtin_call_two, .builtin_call_two_comma => {},
1472 else => break,
1473 }
1474
1475 const first_arg, const second_arg = tree.nodeData(node).opt_node_and_opt_node;
1476 if (first_arg == .none) break; // 0 args
1477 if (second_arg != .none) break; // 2 args
1478
1479 const builtin_token = tree.nodeMainToken(node);
1480 const builtin_name = tree.tokenSlice(builtin_token);
1481 const info = BuiltinFn.list.get(builtin_name) orelse break;
1482
1483 switch (info.tag) {
1484 else => break,
1485 .ptr_cast,
1486 .align_cast,
1487 .addrspace_cast,
1488 .const_cast,
1489 .volatile_cast,
1490 => {},
1491 }
1492
1493 node = first_arg.unwrap().?;
1494 }
1495
1496 return tree.nodeToSpan(node);
1497 },
1498 .node_offset_array_access_index => |node_off| {
1499 const tree = try src_loc.file_scope.getTree(zcu);
1500 const node = node_off.toAbsolute(src_loc.base_node);
1501 return tree.nodeToSpan(tree.nodeData(node).node_and_node[1]);
1502 },
1503 .node_offset_slice_ptr,
1504 .node_offset_slice_start,
1505 .node_offset_slice_end,
1506 .node_offset_slice_sentinel,
1507 => |node_off| {
1508 const tree = try src_loc.file_scope.getTree(zcu);
1509 const node = node_off.toAbsolute(src_loc.base_node);
1510 const full = tree.fullSlice(node).?;
1511 const part_node = switch (src_loc.lazy) {
1512 .node_offset_slice_ptr => full.ast.sliced,
1513 .node_offset_slice_start => full.ast.start,
1514 .node_offset_slice_end => full.ast.end.unwrap().?,
1515 .node_offset_slice_sentinel => full.ast.sentinel.unwrap().?,
1516 else => unreachable,
1517 };
1518 return tree.nodeToSpan(part_node);
1519 },
1520 .node_offset_call_func => |node_off| {
1521 const tree = try src_loc.file_scope.getTree(zcu);
1522 const node = node_off.toAbsolute(src_loc.base_node);
1523 var buf: [1]Ast.Node.Index = undefined;
1524 const full = tree.fullCall(&buf, node).?;
1525 return tree.nodeToSpan(full.ast.fn_expr);
1526 },
1527 .node_offset_field_name => |node_off| {
1528 const tree = try src_loc.file_scope.getTree(zcu);
1529 const node = node_off.toAbsolute(src_loc.base_node);
1530 var buf: [1]Ast.Node.Index = undefined;
1531 const tok_index = switch (tree.nodeTag(node)) {
1532 .field_access => tree.nodeData(node).node_and_token[1],
1533 .call_one,
1534 .call_one_comma,
1535 .call,
1536 .call_comma,
1537 => blk: {
1538 const full = tree.fullCall(&buf, node).?;
1539 break :blk tree.lastToken(full.ast.fn_expr);
1540 },
1541 else => tree.firstToken(node) - 2,
1542 };
1543 const start = tree.tokenStart(tok_index);
1544 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1545 return Span{ .start = start, .end = end, .main = start };
1546 },
1547 .node_offset_field_name_init => |node_off| {
1548 const tree = try src_loc.file_scope.getTree(zcu);
1549 const node = node_off.toAbsolute(src_loc.base_node);
1550 const tok_index = tree.firstToken(node) - 2;
1551 const start = tree.tokenStart(tok_index);
1552 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1553 return Span{ .start = start, .end = end, .main = start };
1554 },
1555 .node_offset_deref_ptr => |node_off| {
1556 const tree = try src_loc.file_scope.getTree(zcu);
1557 const node = node_off.toAbsolute(src_loc.base_node);
1558 return tree.nodeToSpan(tree.nodeData(node).node);
1559 },
1560 .node_offset_asm_source => |node_off| {
1561 const tree = try src_loc.file_scope.getTree(zcu);
1562 const node = node_off.toAbsolute(src_loc.base_node);
1563 const full = tree.fullAsm(node).?;
1564 return tree.nodeToSpan(full.ast.template);
1565 },
1566 .node_offset_asm_ret_ty => |node_off| {
1567 const tree = try src_loc.file_scope.getTree(zcu);
1568 const node = node_off.toAbsolute(src_loc.base_node);
1569 const full = tree.fullAsm(node).?;
1570 const asm_output = full.outputs[0];
1571 return tree.nodeToSpan(tree.nodeData(asm_output).opt_node_and_token[0].unwrap().?);
1572 },
1573
1574 .node_offset_if_cond => |node_off| {
1575 const tree = try src_loc.file_scope.getTree(zcu);
1576 const node = node_off.toAbsolute(src_loc.base_node);
1577 const src_node = switch (tree.nodeTag(node)) {
1578 .if_simple,
1579 .@"if",
1580 => tree.fullIf(node).?.ast.cond_expr,
1581
1582 .while_simple,
1583 .while_cont,
1584 .@"while",
1585 => tree.fullWhile(node).?.ast.cond_expr,
1586
1587 .for_simple,
1588 .@"for",
1589 => {
1590 const inputs = tree.fullFor(node).?.ast.inputs;
1591 const start = tree.firstToken(inputs[0]);
1592 const end = tree.lastToken(inputs[inputs.len - 1]);
1593 return tree.tokensToSpan(start, end, start);
1594 },
1595
1596 .@"orelse" => node,
1597 .@"catch" => node,
1598 else => unreachable,
1599 };
1600 return tree.nodeToSpan(src_node);
1601 },
1602 .asm_input => |input| {
1603 const tree = try src_loc.file_scope.getTree(zcu);
1604 const node = input.offset.toAbsolute(src_loc.base_node);
1605 const full = tree.fullAsm(node).?;
1606 const asm_input = full.inputs[input.input_index];
1607 return tree.nodeToSpan(tree.nodeData(asm_input).node_and_token[0]);
1608 },
1609 .asm_output => |output| {
1610 const tree = try src_loc.file_scope.getTree(zcu);
1611 const node = output.offset.toAbsolute(src_loc.base_node);
1612 const full = tree.fullAsm(node).?;
1613 const asm_output = full.outputs[output.output_index];
1614 const data = tree.nodeData(asm_output).opt_node_and_token;
1615 return if (data[0].unwrap()) |output_node|
1616 tree.nodeToSpan(output_node)
1617 else
1618 // token points to the ')'
1619 tree.tokenToSpan(data[1] - 1);
1620 },
1621 .asm_clobbers => |offset| {
1622 const tree = try src_loc.file_scope.getTree(zcu);
1623 const node = offset.toAbsolute(src_loc.base_node);
1624 const full = tree.fullAsm(node).?;
1625 return tree.nodeToSpan(full.ast.clobbers.unwrap().?); // this should only be reachable if the clobbers are written in the source
1626 },
1627 .for_input => |for_input| {
1628 const tree = try src_loc.file_scope.getTree(zcu);
1629 const node = for_input.for_node_offset.toAbsolute(src_loc.base_node);
1630 const for_full = tree.fullFor(node).?;
1631 const src_node = for_full.ast.inputs[for_input.input_index];
1632 return tree.nodeToSpan(src_node);
1633 },
1634 .for_capture_from_input => |node_off| {
1635 const tree = try src_loc.file_scope.getTree(zcu);
1636 const input_node = node_off.toAbsolute(src_loc.base_node);
1637 // We have to actually linear scan the whole AST to find the for loop
1638 // that contains this input.
1639 const node_tags = tree.nodes.items(.tag);
1640 for (node_tags, 0..) |node_tag, node_usize| {
1641 const node: Ast.Node.Index = @fromBackingInt(@intCast(node_usize));
1642 switch (node_tag) {
1643 .for_simple, .@"for" => {
1644 const for_full = tree.fullFor(node).?;
1645 for (for_full.ast.inputs, 0..) |input, input_index| {
1646 if (input_node == input) {
1647 var count = input_index;
1648 var tok = for_full.payload_token;
1649 while (true) {
1650 switch (tree.tokenTag(tok)) {
1651 .comma => {
1652 count -= 1;
1653 tok += 1;
1654 },
1655 .identifier => {
1656 if (count == 0)
1657 return tree.tokensToSpan(tok, tok + 1, tok);
1658 tok += 1;
1659 },
1660 .asterisk => {
1661 if (count == 0)
1662 return tree.tokensToSpan(tok, tok + 2, tok);
1663 tok += 1;
1664 },
1665 else => unreachable,
1666 }
1667 }
1668 }
1669 }
1670 },
1671 else => continue,
1672 }
1673 } else unreachable;
1674 },
1675 .call_arg => |call_arg| {
1676 const tree = try src_loc.file_scope.getTree(zcu);
1677 const node = call_arg.call_node_offset.toAbsolute(src_loc.base_node);
1678 var buf: [2]Ast.Node.Index = undefined;
1679 const call_full = tree.fullCall(buf[0..1], node) orelse {
1680 assert(tree.nodeTag(node) == .builtin_call);
1681 const call_args_node: Ast.Node.Index = @fromBackingInt(@intCast(tree.extra_data[@backingInt(tree.nodeData(node).extra_range.end) - 1]));
1682 switch (tree.nodeTag(call_args_node)) {
1683 .array_init_one,
1684 .array_init_one_comma,
1685 .array_init_dot_two,
1686 .array_init_dot_two_comma,
1687 .array_init_dot,
1688 .array_init_dot_comma,
1689 .array_init,
1690 .array_init_comma,
1691 => {
1692 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements;
1693 return tree.nodeToSpan(full[call_arg.arg_index]);
1694 },
1695 .struct_init_one,
1696 .struct_init_one_comma,
1697 .struct_init_dot_two,
1698 .struct_init_dot_two_comma,
1699 .struct_init_dot,
1700 .struct_init_dot_comma,
1701 .struct_init,
1702 .struct_init_comma,
1703 => {
1704 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields;
1705 return tree.nodeToSpan(full[call_arg.arg_index]);
1706 },
1707 else => return tree.nodeToSpan(call_args_node),
1708 }
1709 };
1710 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]);
1711 },
1712 .fn_proto_param, .fn_proto_param_type => |fn_proto_param| {
1713 const tree = try src_loc.file_scope.getTree(zcu);
1714 const node = fn_proto_param.fn_proto_node_offset.toAbsolute(src_loc.base_node);
1715 var buf: [1]Ast.Node.Index = undefined;
1716 const full = tree.fullFnProto(&buf, node).?;
1717 var it = full.iterate(tree);
1718 var i: usize = 0;
1719 while (it.next()) |param| : (i += 1) {
1720 if (i != fn_proto_param.param_index) continue;
1721
1722 switch (src_loc.lazy) {
1723 .fn_proto_param_type => if (param.anytype_ellipsis3) |tok| {
1724 return tree.tokenToSpan(tok);
1725 } else {
1726 return tree.nodeToSpan(param.type_expr.?);
1727 },
1728 .fn_proto_param => if (param.anytype_ellipsis3) |tok| {
1729 const first = param.comptime_noalias orelse param.name_token orelse tok;
1730 return tree.tokensToSpan(first, tok, first);
1731 } else {
1732 const first = param.comptime_noalias orelse param.name_token orelse tree.firstToken(param.type_expr.?);
1733 return tree.tokensToSpan(first, tree.lastToken(param.type_expr.?), first);
1734 },
1735 else => unreachable,
1736 }
1737 }
1738 unreachable;
1739 },
1740 .node_offset_bin_lhs => |node_off| {
1741 const tree = try src_loc.file_scope.getTree(zcu);
1742 const node = node_off.toAbsolute(src_loc.base_node);
1743 return tree.nodeToSpan(tree.nodeData(node).node_and_node[0]);
1744 },
1745 .node_offset_bin_rhs => |node_off| {
1746 const tree = try src_loc.file_scope.getTree(zcu);
1747 const node = node_off.toAbsolute(src_loc.base_node);
1748 return tree.nodeToSpan(tree.nodeData(node).node_and_node[1]);
1749 },
1750 .array_cat_lhs, .array_cat_rhs => |cat| {
1751 const tree = try src_loc.file_scope.getTree(zcu);
1752 const node = cat.array_cat_offset.toAbsolute(src_loc.base_node);
1753 const arr_node = if (src_loc.lazy == .array_cat_lhs)
1754 tree.nodeData(node).node_and_node[0]
1755 else
1756 tree.nodeData(node).node_and_node[1];
1757
1758 var buf: [2]Ast.Node.Index = undefined;
1759 switch (tree.nodeTag(arr_node)) {
1760 .array_init_one,
1761 .array_init_one_comma,
1762 .array_init_dot_two,
1763 .array_init_dot_two_comma,
1764 .array_init_dot,
1765 .array_init_dot_comma,
1766 .array_init,
1767 .array_init_comma,
1768 => {
1769 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements;
1770 return tree.nodeToSpan(full[cat.elem_index]);
1771 },
1772 else => return tree.nodeToSpan(arr_node),
1773 }
1774 },
1775
1776 .node_offset_try_operand => |node_off| {
1777 const tree = try src_loc.file_scope.getTree(zcu);
1778 const node = node_off.toAbsolute(src_loc.base_node);
1779 return tree.nodeToSpan(tree.nodeData(node).node);
1780 },
1781
1782 .node_offset_switch_operand => |node_off| {
1783 const tree = try src_loc.file_scope.getTree(zcu);
1784 const node = node_off.toAbsolute(src_loc.base_node);
1785 const condition, _ = tree.nodeData(node).node_and_extra;
1786 return tree.nodeToSpan(condition);
1787 },
1788
1789 .node_offset_switch_else_prong => |node_off| {
1790 const tree = try src_loc.file_scope.getTree(zcu);
1791 const switch_node = node_off.toAbsolute(src_loc.base_node);
1792 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1793 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1794 for (case_nodes) |case_node| {
1795 const case = tree.fullSwitchCase(case_node).?;
1796 if (case.ast.values.len == 0) {
1797 return tree.nodeToSpan(case_node);
1798 }
1799 } else unreachable;
1800 },
1801
1802 .node_offset_switch_range => |node_off| {
1803 const tree = try src_loc.file_scope.getTree(zcu);
1804 const switch_node = node_off.toAbsolute(src_loc.base_node);
1805 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1806 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1807 for (case_nodes) |case_node| {
1808 const case = tree.fullSwitchCase(case_node).?;
1809 for (case.ast.values) |item_node| {
1810 if (tree.nodeTag(item_node) == .switch_range) {
1811 return tree.nodeToSpan(item_node);
1812 }
1813 }
1814 } else unreachable;
1815 },
1816 .node_offset_fn_type_align => |node_off| {
1817 const tree = try src_loc.file_scope.getTree(zcu);
1818 const node = node_off.toAbsolute(src_loc.base_node);
1819 var buf: [1]Ast.Node.Index = undefined;
1820 const full = tree.fullFnProto(&buf, node).?;
1821 return tree.nodeToSpan(full.ast.align_expr.unwrap() orelse node);
1822 },
1823 .node_offset_fn_type_addrspace => |node_off| {
1824 const tree = try src_loc.file_scope.getTree(zcu);
1825 const node = node_off.toAbsolute(src_loc.base_node);
1826 var buf: [1]Ast.Node.Index = undefined;
1827 const full = tree.fullFnProto(&buf, node).?;
1828 return tree.nodeToSpan(full.ast.addrspace_expr.unwrap() orelse node);
1829 },
1830 .node_offset_fn_type_section => |node_off| {
1831 const tree = try src_loc.file_scope.getTree(zcu);
1832 const node = node_off.toAbsolute(src_loc.base_node);
1833 var buf: [1]Ast.Node.Index = undefined;
1834 const full = tree.fullFnProto(&buf, node).?;
1835 return tree.nodeToSpan(full.ast.section_expr.unwrap() orelse node);
1836 },
1837 .node_offset_fn_type_cc => |node_off| {
1838 const tree = try src_loc.file_scope.getTree(zcu);
1839 const node = node_off.toAbsolute(src_loc.base_node);
1840 var buf: [1]Ast.Node.Index = undefined;
1841 const full = tree.fullFnProto(&buf, node).?;
1842 return tree.nodeToSpan(full.ast.callconv_expr.unwrap() orelse node);
1843 },
1844
1845 .node_offset_fn_type_ret_ty => |node_off| {
1846 const tree = try src_loc.file_scope.getTree(zcu);
1847 const node = node_off.toAbsolute(src_loc.base_node);
1848 var buf: [1]Ast.Node.Index = undefined;
1849 const full = tree.fullFnProto(&buf, node).?;
1850 return tree.nodeToSpan(full.ast.return_type.unwrap().?);
1851 },
1852 .node_offset_param => |node_off| {
1853 const tree = try src_loc.file_scope.getTree(zcu);
1854 const node = node_off.toAbsolute(src_loc.base_node);
1855
1856 var first_tok = tree.firstToken(node);
1857 while (true) switch (tree.tokenTag(first_tok - 1)) {
1858 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1859 else => break,
1860 };
1861 return tree.tokensToSpan(
1862 first_tok,
1863 tree.lastToken(node),
1864 first_tok,
1865 );
1866 },
1867 .token_offset_param => |token_off| {
1868 const tree = try src_loc.file_scope.getTree(zcu);
1869 const main_token = tree.nodeMainToken(src_loc.base_node);
1870 const tok_index = token_off.toAbsolute(main_token);
1871
1872 var first_tok = tok_index;
1873 while (true) switch (tree.tokenTag(first_tok - 1)) {
1874 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1875 else => break,
1876 };
1877 return tree.tokensToSpan(
1878 first_tok,
1879 tok_index,
1880 first_tok,
1881 );
1882 },
1883
1884 .node_offset_anyframe_type => |node_off| {
1885 const tree = try src_loc.file_scope.getTree(zcu);
1886 const parent_node = node_off.toAbsolute(src_loc.base_node);
1887 _, const child_type = tree.nodeData(parent_node).token_and_node;
1888 return tree.nodeToSpan(child_type);
1889 },
1890
1891 .node_offset_lib_name => |node_off| {
1892 const tree = try src_loc.file_scope.getTree(zcu);
1893 const parent_node = node_off.toAbsolute(src_loc.base_node);
1894 var buf: [1]Ast.Node.Index = undefined;
1895 const full = tree.fullFnProto(&buf, parent_node).?;
1896 const tok_index = full.lib_name.?;
1897 const start = tree.tokenStart(tok_index);
1898 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1899 return Span{ .start = start, .end = end, .main = start };
1900 },
1901
1902 .node_offset_array_type_len => |node_off| {
1903 const tree = try src_loc.file_scope.getTree(zcu);
1904 const parent_node = node_off.toAbsolute(src_loc.base_node);
1905
1906 const full = tree.fullArrayType(parent_node).?;
1907 return tree.nodeToSpan(full.ast.elem_count);
1908 },
1909 .node_offset_array_type_sentinel => |node_off| {
1910 const tree = try src_loc.file_scope.getTree(zcu);
1911 const parent_node = node_off.toAbsolute(src_loc.base_node);
1912
1913 const full = tree.fullArrayType(parent_node).?;
1914 return tree.nodeToSpan(full.ast.sentinel.unwrap().?);
1915 },
1916 .node_offset_array_type_elem => |node_off| {
1917 const tree = try src_loc.file_scope.getTree(zcu);
1918 const parent_node = node_off.toAbsolute(src_loc.base_node);
1919
1920 const full = tree.fullArrayType(parent_node).?;
1921 return tree.nodeToSpan(full.ast.elem_type);
1922 },
1923 .node_offset_un_op => |node_off| {
1924 const tree = try src_loc.file_scope.getTree(zcu);
1925 const node = node_off.toAbsolute(src_loc.base_node);
1926 return tree.nodeToSpan(tree.nodeData(node).node);
1927 },
1928 .node_offset_ptr_elem => |node_off| {
1929 const tree = try src_loc.file_scope.getTree(zcu);
1930 const parent_node = node_off.toAbsolute(src_loc.base_node);
1931
1932 const full = tree.fullPtrType(parent_node).?;
1933 return tree.nodeToSpan(full.ast.child_type);
1934 },
1935 .node_offset_ptr_sentinel => |node_off| {
1936 const tree = try src_loc.file_scope.getTree(zcu);
1937 const parent_node = node_off.toAbsolute(src_loc.base_node);
1938
1939 const full = tree.fullPtrType(parent_node).?;
1940 return tree.nodeToSpan(full.ast.sentinel.unwrap().?);
1941 },
1942 .node_offset_ptr_align => |node_off| {
1943 const tree = try src_loc.file_scope.getTree(zcu);
1944 const parent_node = node_off.toAbsolute(src_loc.base_node);
1945
1946 const full = tree.fullPtrType(parent_node).?;
1947 return tree.nodeToSpan(full.ast.align_node.unwrap().?);
1948 },
1949 .node_offset_ptr_addrspace => |node_off| {
1950 const tree = try src_loc.file_scope.getTree(zcu);
1951 const parent_node = node_off.toAbsolute(src_loc.base_node);
1952
1953 const full = tree.fullPtrType(parent_node).?;
1954 return tree.nodeToSpan(full.ast.addrspace_node.unwrap().?);
1955 },
1956 .node_offset_ptr_bitoffset => |node_off| {
1957 const tree = try src_loc.file_scope.getTree(zcu);
1958 const parent_node = node_off.toAbsolute(src_loc.base_node);
1959
1960 const full = tree.fullPtrType(parent_node).?;
1961 return tree.nodeToSpan(full.ast.bit_range_start.unwrap().?);
1962 },
1963 .node_offset_ptr_hostsize => |node_off| {
1964 const tree = try src_loc.file_scope.getTree(zcu);
1965 const parent_node = node_off.toAbsolute(src_loc.base_node);
1966
1967 const full = tree.fullPtrType(parent_node).?;
1968 return tree.nodeToSpan(full.ast.bit_range_end.unwrap().?);
1969 },
1970 .node_offset_init_ty => |node_off| {
1971 const tree = try src_loc.file_scope.getTree(zcu);
1972 const parent_node = node_off.toAbsolute(src_loc.base_node);
1973
1974 var buf: [2]Ast.Node.Index = undefined;
1975 const type_expr = if (tree.fullArrayInit(&buf, parent_node)) |array_init|
1976 array_init.ast.type_expr.unwrap().?
1977 else
1978 tree.fullStructInit(&buf, parent_node).?.ast.type_expr.unwrap().?;
1979 return tree.nodeToSpan(type_expr);
1980 },
1981 .node_offset_store_ptr => |node_off| {
1982 const tree = try src_loc.file_scope.getTree(zcu);
1983 const node = node_off.toAbsolute(src_loc.base_node);
1984
1985 switch (tree.nodeTag(node)) {
1986 .assign,
1987 .assign_mul,
1988 .assign_div,
1989 .assign_mod,
1990 .assign_add,
1991 .assign_sub,
1992 .assign_shl,
1993 .assign_shl_sat,
1994 .assign_shr,
1995 .assign_bit_and,
1996 .assign_bit_xor,
1997 .assign_bit_or,
1998 .assign_mul_wrap,
1999 .assign_add_wrap,
2000 .assign_sub_wrap,
2001 .assign_mul_sat,
2002 .assign_add_sat,
2003 .assign_sub_sat,
2004 => return tree.nodeToSpan(tree.nodeData(node).node_and_node[0]),
2005 else => return tree.nodeToSpan(node),
2006 }
2007 },
2008 .node_offset_store_operand => |node_off| {
2009 const tree = try src_loc.file_scope.getTree(zcu);
2010 const node = node_off.toAbsolute(src_loc.base_node);
2011
2012 switch (tree.nodeTag(node)) {
2013 .assign,
2014 .assign_mul,
2015 .assign_div,
2016 .assign_mod,
2017 .assign_add,
2018 .assign_sub,
2019 .assign_shl,
2020 .assign_shl_sat,
2021 .assign_shr,
2022 .assign_bit_and,
2023 .assign_bit_xor,
2024 .assign_bit_or,
2025 .assign_mul_wrap,
2026 .assign_add_wrap,
2027 .assign_sub_wrap,
2028 .assign_mul_sat,
2029 .assign_add_sat,
2030 .assign_sub_sat,
2031 => return tree.nodeToSpan(tree.nodeData(node).node_and_node[1]),
2032 else => return tree.nodeToSpan(node),
2033 }
2034 },
2035 .node_offset_return_operand => |node_off| {
2036 const tree = try src_loc.file_scope.getTree(zcu);
2037 const node = node_off.toAbsolute(src_loc.base_node);
2038 if (tree.nodeTag(node) == .@"return") {
2039 if (tree.nodeData(node).opt_node.unwrap()) |lhs| {
2040 return tree.nodeToSpan(lhs);
2041 }
2042 }
2043 return tree.nodeToSpan(node);
2044 },
2045 .container_arg => {
2046 const tree = try src_loc.file_scope.getTree(zcu);
2047 const node = src_loc.base_node;
2048 var buf: [2]Ast.Node.Index = undefined;
2049 if (tree.fullContainerDecl(&buf, node)) |container_decl| {
2050 const arg_node = container_decl.ast.arg.unwrap() orelse return tree.nodeToSpan(node);
2051 return tree.nodeToSpan(arg_node);
2052 } else if (tree.builtinCallParams(&buf, node)) |args| {
2053 // Builtin calls (`@Enum` etc) should use the first argument.
2054 return tree.nodeToSpan(if (args.len > 0) args[0] else node);
2055 } else {
2056 return tree.nodeToSpan(node);
2057 }
2058 },
2059 .container_field_name,
2060 .container_field_value,
2061 .container_field_type,
2062 .container_field_align,
2063 => |field_idx| {
2064 const tree = try src_loc.file_scope.getTree(zcu);
2065 const node = src_loc.base_node;
2066 var buf: [2]Ast.Node.Index = undefined;
2067 const container_decl = tree.fullContainerDecl(&buf, node) orelse {
2068 // This could be a reification builtin. These are the args we care about:
2069 // * `@Enum(_, _, names, values)`
2070 // * `@Struct(_, _, names, types, values_and_aligns)`
2071 // * `@Union(_, _, names, types, aligns)`
2072 if (tree.builtinCallParams(&buf, node)) |args| {
2073 const builtin_name = tree.tokenSlice(tree.firstToken(node));
2074 const arg_index: ?u3 = if (std.mem.eql(u8, builtin_name, "@Enum")) switch (src_loc.lazy) {
2075 .container_field_name => 2,
2076 .container_field_value => 3,
2077 .container_field_type => null,
2078 .container_field_align => null,
2079 else => unreachable,
2080 } else if (std.mem.eql(u8, builtin_name, "@Struct")) switch (src_loc.lazy) {
2081 .container_field_name => 2,
2082 .container_field_value => 4,
2083 .container_field_type => 3,
2084 .container_field_align => 4,
2085 else => unreachable,
2086 } else if (std.mem.eql(u8, builtin_name, "@Union")) switch (src_loc.lazy) {
2087 .container_field_name => 2,
2088 .container_field_value => 4,
2089 .container_field_type => 3,
2090 .container_field_align => null,
2091 else => unreachable,
2092 } else null;
2093 if (arg_index) |i| {
2094 if (args.len >= i) return tree.nodeToSpan(args[i]);
2095 }
2096 }
2097 return tree.nodeToSpan(node);
2098 };
2099
2100 var cur_field_idx: usize = 0;
2101 for (container_decl.ast.members) |member_node| {
2102 const field = tree.fullContainerField(member_node) orelse continue;
2103 if (cur_field_idx < field_idx) {
2104 cur_field_idx += 1;
2105 continue;
2106 }
2107 const field_component_node = switch (src_loc.lazy) {
2108 .container_field_name => .none,
2109 .container_field_value => field.ast.value_expr,
2110 .container_field_type => field.ast.type_expr,
2111 .container_field_align => field.ast.align_expr,
2112 else => unreachable,
2113 };
2114 if (field_component_node.unwrap()) |component_node| {
2115 return tree.nodeToSpan(component_node);
2116 } else {
2117 return tree.tokenToSpan(field.ast.main_token);
2118 }
2119 } else unreachable;
2120 },
2121 .tuple_field_type, .tuple_field_init => |field_info| {
2122 const tree = try src_loc.file_scope.getTree(zcu);
2123 const node = field_info.tuple_decl_node_offset.toAbsolute(src_loc.base_node);
2124 var buf: [2]Ast.Node.Index = undefined;
2125 const container_decl = tree.fullContainerDecl(&buf, node) orelse
2126 return tree.nodeToSpan(node);
2127
2128 const field = tree.fullContainerField(container_decl.ast.members[field_info.elem_index]).?;
2129 return tree.nodeToSpan(switch (src_loc.lazy) {
2130 .tuple_field_type => field.ast.type_expr.unwrap().?,
2131 .tuple_field_init => field.ast.value_expr.unwrap().?,
2132 else => unreachable,
2133 });
2134 },
2135 .init_elem => |init_elem| {
2136 const tree = try src_loc.file_scope.getTree(zcu);
2137 const init_node = init_elem.init_node_offset.toAbsolute(src_loc.base_node);
2138 var buf: [2]Ast.Node.Index = undefined;
2139 if (tree.fullArrayInit(&buf, init_node)) |full| {
2140 const elem_node = full.ast.elements[init_elem.elem_index];
2141 return tree.nodeToSpan(elem_node);
2142 } else if (tree.fullStructInit(&buf, init_node)) |full| {
2143 const field_node = full.ast.fields[init_elem.elem_index];
2144 return tree.tokensToSpan(
2145 tree.firstToken(field_node) - 3,
2146 tree.lastToken(field_node),
2147 tree.nodeMainToken(field_node) - 2,
2148 );
2149 } else unreachable;
2150 },
2151 .init_field_name,
2152 .init_field_linkage,
2153 .init_field_section,
2154 .init_field_visibility,
2155 .init_field_rw,
2156 .init_field_locality,
2157 .init_field_cache,
2158 .init_field_library,
2159 .init_field_thread_local,
2160 .init_field_dll_import,
2161 .init_field_relocation,
2162 .init_field_decoration,
2163 => |builtin_call_node| {
2164 const wanted = switch (src_loc.lazy) {
2165 .init_field_name => "name",
2166 .init_field_linkage => "linkage",
2167 .init_field_section => "section",
2168 .init_field_visibility => "visibility",
2169 .init_field_rw => "rw",
2170 .init_field_locality => "locality",
2171 .init_field_cache => "cache",
2172 .init_field_library => "library",
2173 .init_field_thread_local => "thread_local",
2174 .init_field_dll_import => "dll_import",
2175 .init_field_relocation => "relocation",
2176 .init_field_decoration => "decoration",
2177 else => unreachable,
2178 };
2179 const tree = try src_loc.file_scope.getTree(zcu);
2180 const node = builtin_call_node.toAbsolute(src_loc.base_node);
2181 var builtin_buf: [2]Ast.Node.Index = undefined;
2182 const args = tree.builtinCallParams(&builtin_buf, node).?;
2183 const arg_node = args[1];
2184 var buf: [2]Ast.Node.Index = undefined;
2185 const full = tree.fullStructInit(&buf, arg_node) orelse
2186 return tree.nodeToSpan(arg_node);
2187 for (full.ast.fields) |field_node| {
2188 // . IDENTIFIER = field_node
2189 const name_token = tree.firstToken(field_node) - 2;
2190 const name = tree.tokenSlice(name_token);
2191 if (std.mem.eql(u8, name, wanted)) {
2192 return tree.tokensToSpan(
2193 name_token - 1,
2194 tree.lastToken(field_node),
2195 tree.nodeMainToken(field_node) - 2,
2196 );
2197 }
2198 }
2199 return tree.nodeToSpan(arg_node);
2200 },
2201 .switch_case_item,
2202 .switch_case_item_range_first,
2203 .switch_case_item_range_last,
2204 .switch_capture,
2205 .switch_tag_capture,
2206 => {
2207 const switch_node_offset, const want_case_idx = switch (src_loc.lazy) {
2208 .switch_case_item,
2209 .switch_case_item_range_first,
2210 .switch_case_item_range_last,
2211 => |x| .{ x.switch_node_offset, x.case_idx },
2212 .switch_capture,
2213 .switch_tag_capture,
2214 => |x| .{ x.switch_node_offset, x.case_idx },
2215 else => unreachable,
2216 };
2217
2218 const tree = try src_loc.file_scope.getTree(zcu);
2219 const switch_node = switch_node_offset.toAbsolute(src_loc.base_node);
2220 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
2221 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
2222
2223 var multi_i: u32 = 0;
2224 var scalar_i: u32 = 0;
2225 const case: Ast.full.SwitchCase = case: for (case_nodes) |case_node| {
2226 const case = tree.fullSwitchCase(case_node).?;
2227 if (case.ast.values.len == 0) {
2228 if (want_case_idx == Zir.UnwrappedSwitchBlock.Case.Index.@"else") {
2229 break :case case;
2230 }
2231 continue :case;
2232 }
2233
2234 const is_multi = case.ast.values.len != 1 or
2235 tree.nodeTag(case.ast.values[0]) == .switch_range;
2236
2237 switch (want_case_idx.kind) {
2238 .scalar => if (!is_multi and want_case_idx.value == scalar_i)
2239 break :case case,
2240 .multi => if (is_multi and want_case_idx.value == multi_i)
2241 break :case case,
2242 }
2243
2244 if (is_multi) {
2245 multi_i += 1;
2246 } else {
2247 scalar_i += 1;
2248 }
2249 } else unreachable;
2250
2251 const want_item_idx = switch (src_loc.lazy) {
2252 .switch_case_item,
2253 .switch_case_item_range_first,
2254 .switch_case_item_range_last,
2255 => |x| item_idx: {
2256 assert(want_case_idx != Zir.UnwrappedSwitchBlock.Case.Index.@"else");
2257 break :item_idx x.item_idx;
2258 },
2259 .switch_capture, .switch_tag_capture => {
2260 const start = switch (src_loc.lazy) {
2261 .switch_capture => case.payload_token.?,
2262 .switch_tag_capture => tok: {
2263 var tok = case.payload_token.?;
2264 if (tree.tokenTag(tok) == .asterisk) tok += 1;
2265 tok = tok + 2; // skip over comma
2266 break :tok tok;
2267 },
2268 else => unreachable,
2269 };
2270 const end = switch (tree.tokenTag(start)) {
2271 .asterisk => start + 1,
2272 else => start,
2273 };
2274 return tree.tokensToSpan(start, end, start);
2275 },
2276 else => unreachable,
2277 };
2278
2279 switch (want_item_idx.kind) {
2280 .single => {
2281 var item_i: u32 = 0;
2282 for (case.ast.values) |item_node| {
2283 if (tree.nodeTag(item_node) == .switch_range) {
2284 continue;
2285 }
2286 if (item_i != want_item_idx.value) {
2287 item_i += 1;
2288 continue;
2289 }
2290 return tree.nodeToSpan(item_node);
2291 } else unreachable;
2292 },
2293 .range => {
2294 var range_i: u32 = 0;
2295 for (case.ast.values) |item_node| {
2296 if (tree.nodeTag(item_node) != .switch_range) {
2297 continue;
2298 }
2299 if (range_i != want_item_idx.value) {
2300 range_i += 1;
2301 continue;
2302 }
2303 const first, const last = tree.nodeData(item_node).node_and_node;
2304 return switch (src_loc.lazy) {
2305 .switch_case_item => tree.nodeToSpan(item_node),
2306 .switch_case_item_range_first => tree.nodeToSpan(first),
2307 .switch_case_item_range_last => tree.nodeToSpan(last),
2308 else => unreachable,
2309 };
2310 } else unreachable;
2311 },
2312 }
2313 },
2314 .func_decl_param_comptime => |param_idx| {
2315 const tree = try src_loc.file_scope.getTree(zcu);
2316 var buf: [1]Ast.Node.Index = undefined;
2317 const full = tree.fullFnProto(&buf, src_loc.base_node).?;
2318 var param_it = full.iterate(tree);
2319 for (0..param_idx) |_| assert(param_it.next() != null);
2320 const param = param_it.next().?;
2321 return tree.tokenToSpan(param.comptime_noalias.?);
2322 },
2323 .func_decl_param_ty => |param_idx| {
2324 const tree = try src_loc.file_scope.getTree(zcu);
2325 var buf: [1]Ast.Node.Index = undefined;
2326 const full = tree.fullFnProto(&buf, src_loc.base_node).?;
2327 var param_it = full.iterate(tree);
2328 for (0..param_idx) |_| assert(param_it.next() != null);
2329 const param = param_it.next().?;
2330 if (param.anytype_ellipsis3) |tok| {
2331 return tree.tokenToSpan(tok);
2332 } else {
2333 return tree.nodeToSpan(param.type_expr.?);
2334 }
2335 },
2336 }
2337 }
2338};
2339
2340pub const LazySrcLoc = struct {
2341 /// This instruction provides the source node locations are resolved relative to.
2342 /// It is a `declaration`, `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl`.
2343 /// This must be valid even if `relative` is an absolute value, since it is required to
2344 /// determine the file which the `LazySrcLoc` refers to.
2345 base_node_inst: InternPool.TrackedInst.Index,
2346 /// This field determines the source location relative to `base_node_inst`.
2347 offset: Offset,
2348
2349 pub const Offset = union(enum) {
2350 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
2351 /// that all code paths which would need to resolve the source location are
2352 /// unreachable. If you are debugging this tag incorrectly being this value,
2353 /// look into using reverse-continue with a memory watchpoint to see where the
2354 /// value is being set to this tag.
2355 /// `base_node_inst` is unused.
2356 unneeded,
2357 /// The source location points to a byte offset within a source file,
2358 /// offset from 0. The source file is determined contextually.
2359 byte_abs: u32,
2360 /// The source location points to a token within a source file,
2361 /// offset from 0. The source file is determined contextually.
2362 token_abs: Ast.TokenIndex,
2363 /// The source location points to an AST node within a source file,
2364 /// offset from 0. The source file is determined contextually.
2365 node_abs: Ast.Node.Index,
2366 /// The source location points to a byte offset within a source file,
2367 /// offset from the byte offset of the base node within the file.
2368 byte_offset: u32,
2369 /// This data is the offset into the token list from the base node's first token.
2370 token_offset: Ast.TokenOffset,
2371 /// The source location points to an AST node, which is this value offset
2372 /// from its containing base node AST index.
2373 node_offset: TracedOffset,
2374 /// The source location points to the main token of an AST node, found
2375 /// by taking this AST node index offset from the containing base node.
2376 node_offset_main_token: Ast.Node.Offset,
2377 /// The source location points to the beginning of a struct initializer.
2378 node_offset_initializer: Ast.Node.Offset,
2379 /// The source location points to a variable declaration type expression,
2380 /// found by taking this AST node index offset from the containing
2381 /// base node, which points to a variable declaration AST node. Next, navigate
2382 /// to the type expression.
2383 node_offset_var_decl_ty: Ast.Node.Offset,
2384 /// The source location points to the alignment expression of a var decl.
2385 node_offset_var_decl_align: Ast.Node.Offset,
2386 /// The source location points to the linksection expression of a var decl.
2387 node_offset_var_decl_section: Ast.Node.Offset,
2388 /// The source location points to the addrspace expression of a var decl.
2389 node_offset_var_decl_addrspace: Ast.Node.Offset,
2390 /// The source location points to the initializer of a var decl.
2391 node_offset_var_decl_init: Ast.Node.Offset,
2392 /// The source location points to the given argument of a builtin function call.
2393 /// `builtin_call_node` points to the builtin call.
2394 /// `arg_index` is the index of the argument which hte source location refers to.
2395 node_offset_builtin_call_arg: struct {
2396 builtin_call_node: Ast.Node.Offset,
2397 arg_index: u32,
2398 },
2399 /// Like `node_offset_builtin_call_arg` but recurses through arbitrarily many calls
2400 /// to pointer cast builtins (taking the first argument of the most nested).
2401 node_offset_ptrcast_operand: Ast.Node.Offset,
2402 /// The source location points to the index expression of an array access
2403 /// expression, found by taking this AST node index offset from the containing
2404 /// base node, which points to an array access AST node. Next, navigate
2405 /// to the index expression.
2406 node_offset_array_access_index: Ast.Node.Offset,
2407 /// The source location points to the LHS of a slice expression
2408 /// expression, found by taking this AST node index offset from the containing
2409 /// base node, which points to a slice AST node. Next, navigate
2410 /// to the sentinel expression.
2411 node_offset_slice_ptr: Ast.Node.Offset,
2412 /// The source location points to start expression of a slice expression
2413 /// expression, found by taking this AST node index offset from the containing
2414 /// base node, which points to a slice AST node. Next, navigate
2415 /// to the sentinel expression.
2416 node_offset_slice_start: Ast.Node.Offset,
2417 /// The source location points to the end expression of a slice
2418 /// expression, found by taking this AST node index offset from the containing
2419 /// base node, which points to a slice AST node. Next, navigate
2420 /// to the sentinel expression.
2421 node_offset_slice_end: Ast.Node.Offset,
2422 /// The source location points to the sentinel expression of a slice
2423 /// expression, found by taking this AST node index offset from the containing
2424 /// base node, which points to a slice AST node. Next, navigate
2425 /// to the sentinel expression.
2426 node_offset_slice_sentinel: Ast.Node.Offset,
2427 /// The source location points to the callee expression of a function
2428 /// call expression, found by taking this AST node index offset from the containing
2429 /// base node, which points to a function call AST node. Next, navigate
2430 /// to the callee expression.
2431 node_offset_call_func: Ast.Node.Offset,
2432 /// The payload is offset from the containing base node.
2433 /// The source location points to the field name of:
2434 /// * a field access expression (`a.b`), or
2435 /// * the callee of a method call (`a.b()`)
2436 node_offset_field_name: Ast.Node.Offset,
2437 /// The payload is offset from the containing base node.
2438 /// The source location points to the field name of the operand ("b" node)
2439 /// of a field initialization expression (`.a = b`)
2440 node_offset_field_name_init: Ast.Node.Offset,
2441 /// The source location points to the pointer of a pointer deref expression,
2442 /// found by taking this AST node index offset from the containing
2443 /// base node, which points to a pointer deref AST node. Next, navigate
2444 /// to the pointer expression.
2445 node_offset_deref_ptr: Ast.Node.Offset,
2446 /// The source location points to the assembly source code of an inline assembly
2447 /// expression, found by taking this AST node index offset from the containing
2448 /// base node, which points to inline assembly AST node. Next, navigate
2449 /// to the asm template source code.
2450 node_offset_asm_source: Ast.Node.Offset,
2451 /// The source location points to the return type of an inline assembly
2452 /// expression, found by taking this AST node index offset from the containing
2453 /// base node, which points to inline assembly AST node. Next, navigate
2454 /// to the return type expression.
2455 node_offset_asm_ret_ty: Ast.Node.Offset,
2456 /// The source location points to the condition expression of an if
2457 /// expression, found by taking this AST node index offset from the containing
2458 /// base node, which points to an if expression AST node. Next, navigate
2459 /// to the condition expression.
2460 node_offset_if_cond: Ast.Node.Offset,
2461 /// The source location points to a binary expression, such as `a + b`, found
2462 /// by taking this AST node index offset from the containing base node.
2463 node_offset_bin_op: Ast.Node.Offset,
2464 /// The source location points to the LHS of a binary expression, found
2465 /// by taking this AST node index offset from the containing base node,
2466 /// which points to a binary expression AST node. Next, navigate to the LHS.
2467 node_offset_bin_lhs: Ast.Node.Offset,
2468 /// The source location points to the RHS of a binary expression, found
2469 /// by taking this AST node index offset from the containing base node,
2470 /// which points to a binary expression AST node. Next, navigate to the RHS.
2471 node_offset_bin_rhs: Ast.Node.Offset,
2472 /// The source location points to the operand of a try expression, found
2473 /// by taking this AST node index offset from the containing base node,
2474 /// which points to a try expression AST node. Next, navigate to the
2475 /// operand expression.
2476 node_offset_try_operand: Ast.Node.Offset,
2477 /// The source location points to the operand of a switch expression, found
2478 /// by taking this AST node index offset from the containing base node,
2479 /// which points to a switch expression AST node. Next, navigate to the operand.
2480 node_offset_switch_operand: Ast.Node.Offset,
2481 /// The source location points to the else prong of a switch expression, found
2482 /// by taking this AST node index offset from the containing base node,
2483 /// which points to a switch expression AST node. Next, navigate to the else prong.
2484 node_offset_switch_else_prong: Ast.Node.Offset,
2485 /// The source location points to all the ranges of a switch expression, found
2486 /// by taking this AST node index offset from the containing base node,
2487 /// which points to a switch expression AST node. Next, navigate to any of the
2488 /// range nodes. The error applies to all of them.
2489 node_offset_switch_range: Ast.Node.Offset,
2490 /// The source location points to the align expr of a function type
2491 /// expression, found by taking this AST node index offset from the containing
2492 /// base node, which points to a function type AST node. Next, navigate to
2493 /// the calling convention node.
2494 node_offset_fn_type_align: Ast.Node.Offset,
2495 /// The source location points to the addrspace expr of a function type
2496 /// expression, found by taking this AST node index offset from the containing
2497 /// base node, which points to a function type AST node. Next, navigate to
2498 /// the calling convention node.
2499 node_offset_fn_type_addrspace: Ast.Node.Offset,
2500 /// The source location points to the linksection expr of a function type
2501 /// expression, found by taking this AST node index offset from the containing
2502 /// base node, which points to a function type AST node. Next, navigate to
2503 /// the calling convention node.
2504 node_offset_fn_type_section: Ast.Node.Offset,
2505 /// The source location points to the calling convention of a function type
2506 /// expression, found by taking this AST node index offset from the containing
2507 /// base node, which points to a function type AST node. Next, navigate to
2508 /// the calling convention node.
2509 node_offset_fn_type_cc: Ast.Node.Offset,
2510 /// The source location points to the return type of a function type
2511 /// expression, found by taking this AST node index offset from the containing
2512 /// base node, which points to a function type AST node. Next, navigate to
2513 /// the return type node.
2514 node_offset_fn_type_ret_ty: Ast.Node.Offset,
2515 node_offset_param: Ast.Node.Offset,
2516 token_offset_param: Ast.TokenOffset,
2517 /// The source location points to the type expression of an `anyframe->T`
2518 /// expression, found by taking this AST node index offset from the containing
2519 /// base node, which points to a `anyframe->T` expression AST node. Next, navigate
2520 /// to the type expression.
2521 node_offset_anyframe_type: Ast.Node.Offset,
2522 /// The source location points to the string literal of `extern "foo"`, found
2523 /// by taking this AST node index offset from the containing
2524 /// base node, which points to a function prototype or variable declaration
2525 /// expression AST node. Next, navigate to the string literal of the `extern "foo"`.
2526 node_offset_lib_name: Ast.Node.Offset,
2527 /// The source location points to the len expression of an `[N:S]T`
2528 /// expression, found by taking this AST node index offset from the containing
2529 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2530 /// to the len expression.
2531 node_offset_array_type_len: Ast.Node.Offset,
2532 /// The source location points to the sentinel expression of an `[N:S]T`
2533 /// expression, found by taking this AST node index offset from the containing
2534 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2535 /// to the sentinel expression.
2536 node_offset_array_type_sentinel: Ast.Node.Offset,
2537 /// The source location points to the elem expression of an `[N:S]T`
2538 /// expression, found by taking this AST node index offset from the containing
2539 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2540 /// to the elem expression.
2541 node_offset_array_type_elem: Ast.Node.Offset,
2542 /// The source location points to the operand of an unary expression.
2543 node_offset_un_op: Ast.Node.Offset,
2544 /// The source location points to the elem type of a pointer.
2545 node_offset_ptr_elem: Ast.Node.Offset,
2546 /// The source location points to the sentinel of a pointer.
2547 node_offset_ptr_sentinel: Ast.Node.Offset,
2548 /// The source location points to the align expr of a pointer.
2549 node_offset_ptr_align: Ast.Node.Offset,
2550 /// The source location points to the addrspace expr of a pointer.
2551 node_offset_ptr_addrspace: Ast.Node.Offset,
2552 /// The source location points to the bit-offset of a pointer.
2553 node_offset_ptr_bitoffset: Ast.Node.Offset,
2554 /// The source location points to the host size of a pointer.
2555 node_offset_ptr_hostsize: Ast.Node.Offset,
2556 /// The source location points to the type of an array or struct initializer.
2557 node_offset_init_ty: Ast.Node.Offset,
2558 /// The source location points to the LHS of an assignment (or assign-op, e.g. `+=`).
2559 node_offset_store_ptr: Ast.Node.Offset,
2560 /// The source location points to the RHS of an assignment (or assign-op, e.g. `+=`).
2561 node_offset_store_operand: Ast.Node.Offset,
2562 /// The source location points to the operand of a `return` statement, or
2563 /// the `return` itself if there is no explicit operand.
2564 node_offset_return_operand: Ast.Node.Offset,
2565 /// The source location points to an assembly input
2566 asm_input: struct {
2567 /// Points to the assembly node
2568 offset: Ast.Node.Offset,
2569 input_index: u32,
2570 },
2571 /// The source location points to an assembly output
2572 asm_output: struct {
2573 /// Points to the assembly node
2574 offset: Ast.Node.Offset,
2575 output_index: u32,
2576 },
2577 /// Points to the assembly node
2578 asm_clobbers: Ast.Node.Offset,
2579 /// The source location points to a for loop input.
2580 for_input: struct {
2581 /// Points to the for loop AST node.
2582 for_node_offset: Ast.Node.Offset,
2583 /// Picks one of the inputs from the condition.
2584 input_index: u32,
2585 },
2586 /// The source location points to one of the captures of a for loop, found
2587 /// by taking this AST node index offset from the containing
2588 /// base node, which points to one of the input nodes of a for loop.
2589 /// Next, navigate to the corresponding capture.
2590 for_capture_from_input: Ast.Node.Offset,
2591 /// The source location points to the argument node of a function call.
2592 call_arg: struct {
2593 /// Points to the function call AST node.
2594 call_node_offset: Ast.Node.Offset,
2595 /// The index of the argument the source location points to.
2596 arg_index: u32,
2597 },
2598 fn_proto_param: FnProtoParam,
2599 fn_proto_param_type: FnProtoParam,
2600 array_cat_lhs: ArrayCat,
2601 array_cat_rhs: ArrayCat,
2602 /// The source location points to the backing or tag type expression of
2603 /// the container type declaration at the base node.
2604 ///
2605 /// For 'union(enum(T))', this points to 'T', not 'enum(T)'.
2606 container_arg,
2607 /// The source location points to the name of the field at the given index
2608 /// of the container type declaration at the base node.
2609 container_field_name: u32,
2610 /// Like `continer_field_name`, but points at the field's default value.
2611 container_field_value: u32,
2612 /// Like `continer_field_name`, but points at the field's type.
2613 container_field_type: u32,
2614 /// Like `continer_field_name`, but points at the field's alignment.
2615 container_field_align: u32,
2616 /// The source location points to the type of the field at the given index
2617 /// of the tuple type declaration at `tuple_decl_node_offset`.
2618 tuple_field_type: TupleField,
2619 /// The source location points to the default init of the field at the given index
2620 /// of the tuple type declaration at `tuple_decl_node_offset`.
2621 tuple_field_init: TupleField,
2622 /// The source location points to the given element/field of a struct or
2623 /// array initialization expression.
2624 init_elem: struct {
2625 /// Points to the AST node of the initialization expression.
2626 init_node_offset: Ast.Node.Offset,
2627 /// The index of the field/element the source location points to.
2628 elem_index: u32,
2629 },
2630 // The following source locations are like `init_elem`, but refer to a
2631 // field with a specific name. If such a field is not given, the entire
2632 // initialization expression is used instead.
2633 // The `Ast.Node.Offset` points to the AST node of a builtin call, whose *second*
2634 // argument is the init expression.
2635 init_field_name: Ast.Node.Offset,
2636 init_field_linkage: Ast.Node.Offset,
2637 init_field_section: Ast.Node.Offset,
2638 init_field_visibility: Ast.Node.Offset,
2639 init_field_rw: Ast.Node.Offset,
2640 init_field_locality: Ast.Node.Offset,
2641 init_field_cache: Ast.Node.Offset,
2642 init_field_library: Ast.Node.Offset,
2643 init_field_thread_local: Ast.Node.Offset,
2644 init_field_dll_import: Ast.Node.Offset,
2645 init_field_relocation: Ast.Node.Offset,
2646 init_field_decoration: Ast.Node.Offset,
2647 /// The source location points to the value of an item in a specific
2648 /// case of a `switch`.
2649 switch_case_item: SwitchItem,
2650 /// The source location points to the "first" value of a range item in
2651 /// a specific case of a `switch`.
2652 switch_case_item_range_first: SwitchItem,
2653 /// The source location points to the "last" value of a range item in
2654 /// a specific case of a `switch`.
2655 switch_case_item_range_last: SwitchItem,
2656 /// The source location points to the main capture of a specific case of
2657 /// a `switch`.
2658 switch_capture: SwitchCapture,
2659 /// The source location points to the "tag" capture (second capture) of
2660 /// a specific case of a `switch`.
2661 switch_tag_capture: SwitchCapture,
2662 /// The source location points to the `comptime` token on the given comptime parameter,
2663 /// where the base node is a function declaration. The value is the parameter index.
2664 func_decl_param_comptime: u32,
2665 /// The source location points to the type annotation on the given function parameter,
2666 /// where the base node is a function declaration. The value is the parameter index.
2667 func_decl_param_ty: u32,
2668
2669 pub const FnProtoParam = struct {
2670 /// The offset of the function prototype AST node.
2671 fn_proto_node_offset: Ast.Node.Offset,
2672 /// The index of the parameter the source location points to.
2673 param_index: u32,
2674 };
2675
2676 pub const SwitchItem = struct {
2677 /// The offset of the switch AST node.
2678 switch_node_offset: Ast.Node.Offset,
2679 /// The index of the case to point to within this switch.
2680 case_idx: Zir.UnwrappedSwitchBlock.Case.Index,
2681 /// The index of the item to point to within this case.
2682 item_idx: SwitchItem.Index,
2683
2684 pub const Index = packed struct(u32) {
2685 kind: enum(u1) { single, range },
2686 value: u31,
2687 };
2688 };
2689
2690 pub const SwitchCapture = struct {
2691 /// The offset of the switch AST node.
2692 switch_node_offset: Ast.Node.Offset,
2693 /// The index of the case whose capture to point to.
2694 case_idx: Zir.UnwrappedSwitchBlock.Case.Index,
2695 };
2696
2697 pub const ArrayCat = struct {
2698 /// Points to the array concat AST node.
2699 array_cat_offset: Ast.Node.Offset,
2700 /// The index of the element the source location points to.
2701 elem_index: u32,
2702 };
2703
2704 pub const TupleField = struct {
2705 /// Points to the AST node of the tuple type decaration.
2706 tuple_decl_node_offset: Ast.Node.Offset,
2707 /// The index of the tuple field the source location points to.
2708 elem_index: u32,
2709 };
2710
2711 pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
2712
2713 noinline fn nodeOffsetDebug(node_offset: Ast.Node.Offset) Offset {
2714 var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } };
2715 result.node_offset.trace.addAddr(@returnAddress(), "init");
2716 return result;
2717 }
2718
2719 fn nodeOffsetRelease(node_offset: Ast.Node.Offset) Offset {
2720 return .{ .node_offset = .{ .x = node_offset } };
2721 }
2722
2723 /// This wraps a simple integer in debug builds so that later on we can find out
2724 /// where in semantic analysis the value got set.
2725 pub const TracedOffset = struct {
2726 x: Ast.Node.Offset,
2727 trace: std.debug.Trace = std.debug.Trace.init,
2728
2729 const want_tracing = false;
2730 };
2731 };
2732
2733 pub const unneeded: LazySrcLoc = .{
2734 .base_node_inst = undefined,
2735 .offset = .unneeded,
2736 };
2737
2738 /// Returns `null` if the ZIR instruction has been lost across incremental updates.
2739 pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) ?struct { *File, Ast.Node.Index } {
2740 comptime assert(Zir.inst_tracking_version == 0);
2741
2742 const ip = &zcu.intern_pool;
2743 const file_index, const zir_inst = inst: {
2744 const info = base_node_inst.resolveFull(ip) orelse return null;
2745 break :inst .{ info.file, info.inst };
2746 };
2747 const file = zcu.fileByIndex(file_index);
2748
2749 // If we're relative to .main_struct_inst, we know the ast node is the root and don't need to resolve the ZIR,
2750 // which may not exist e.g. in the case of errors in ZON files.
2751 if (zir_inst == .main_struct_inst) return .{ file, .root };
2752
2753 // Otherwise, make sure ZIR is loaded.
2754 const zir = file.zir.?;
2755
2756 const inst = zir.instructions.get(@backingInt(zir_inst));
2757 const base_node: Ast.Node.Index = switch (inst.tag) {
2758 .declaration => inst.data.declaration.src_node,
2759 .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_node,
2760 .struct_init_anon => zir.extraData(Zir.Inst.StructInitAnon, inst.data.pl_node.payload_index).data.abs_node,
2761 .extended => switch (inst.data.extended.opcode) {
2762 .struct_decl => zir.getStructDecl(zir_inst).src_node,
2763 .union_decl => zir.getUnionDecl(zir_inst).src_node,
2764 .enum_decl => zir.getEnumDecl(zir_inst).src_node,
2765 .opaque_decl => zir.getOpaqueDecl(zir_inst).src_node,
2766 .reify_enum => zir.extraData(Zir.Inst.ReifyEnum, inst.data.extended.operand).data.node,
2767 .reify_struct => zir.extraData(Zir.Inst.ReifyStruct, inst.data.extended.operand).data.node,
2768 .reify_union => zir.extraData(Zir.Inst.ReifyUnion, inst.data.extended.operand).data.node,
2769 .reify_spirv_type => zir.extraData(Zir.Inst.ReifySpirvType, inst.data.extended.operand).data.node,
2770 else => unreachable,
2771 },
2772 else => unreachable,
2773 };
2774 return .{ file, base_node };
2775 }
2776
2777 /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`.
2778 /// The resulting `SrcLoc` should only be used ephemerally, as it is not correct across incremental updates.
2779 pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc {
2780 return lazy.upgradeOrLost(zcu).?;
2781 }
2782
2783 /// Like `upgrade`, but returns `null` if the source location has been lost across incremental updates.
2784 pub fn upgradeOrLost(lazy: LazySrcLoc, zcu: *Zcu) ?SrcLoc {
2785 const file, const base_node: Ast.Node.Index = resolveBaseNode(lazy.base_node_inst, zcu) orelse return null;
2786 return .{
2787 .file_scope = file,
2788 .base_node = base_node,
2789 .lazy = lazy.offset,
2790 };
2791 }
2792
2793 pub fn order(lhs: LazySrcLoc, rhs: LazySrcLoc, zcu: *Zcu) std.math.Order {
2794 const lhs_resolved = lhs.upgradeOrLost(zcu) orelse {
2795 // LHS source location lost, so should never be referenced. Just sort it to the end.
2796 return .gt;
2797 };
2798 const rhs_resolved = rhs.upgradeOrLost(zcu) orelse {
2799 // RHS source location lost, so should never be referenced. Just sort it to the end.
2800 return .lt;
2801 };
2802 if (lhs_resolved.file_scope != rhs_resolved.file_scope) {
2803 const lhs_path = lhs_resolved.file_scope.path;
2804 const rhs_path = rhs_resolved.file_scope.path;
2805 return std.math.order(@backingInt(lhs_path.root), @backingInt(rhs_path.root)).differ() orelse
2806 std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).differ().?;
2807 }
2808 const prev_prot = zcu.comp.io.swapCancelProtection(.blocked);
2809 defer _ = zcu.comp.io.swapCancelProtection(prev_prot);
2810 const lhs_span = lhs_resolved.span(zcu) catch |err| {
2811 assert(err != error.Canceled); // we're protected
2812 // Failed to read LHS, so we'll get a transient error. Just sort it to the end.
2813 return .gt;
2814 };
2815 const rhs_span = rhs_resolved.span(zcu) catch |err| {
2816 assert(err != error.Canceled); // we're protected
2817 // Failed to read RHS, so we'll get a transient error. Just sort it to the end.
2818 return .lt;
2819 };
2820 return std.math.order(lhs_span.main, rhs_span.main);
2821 }
2822};
2823
2824pub const SemaError = error{ OutOfMemory, Canceled, AlreadyReported };
2825pub const CompileError = error{
2826 OutOfMemory,
2827 /// The compilation update is no longer desired.
2828 Canceled,
2829 /// When this is returned, the compile error for the failure has already been recorded.
2830 AlreadyReported,
2831 /// In a comptime scope, a return instruction was encountered. This error is only seen when
2832 /// doing a comptime function call.
2833 ComptimeReturn,
2834 /// In a comptime scope, a break instruction was encountered. This error is only seen when
2835 /// evaluating a comptime block.
2836 ComptimeBreak,
2837};
2838
2839pub fn init(zcu: *Zcu, gpa: Allocator, io: Io, thread_count: usize) !void {
2840 try zcu.intern_pool.init(gpa, io, thread_count);
2841}
2842
2843/// It is valid to not call this function before `deinit` in error paths.
2844/// Requires the fields on `zcu.comp` to already be initialized.
2845pub fn initAfterCompilation(zcu: *Zcu) void {
2846 zcu.initTracyPlots();
2847}
2848
2849pub fn deinit(zcu: *Zcu) void {
2850 const comp = zcu.comp;
2851 const io = comp.io;
2852 const gpa = zcu.gpa;
2853 {
2854 if (zcu.llvm_object) |llvm_object| llvm_object.deinit();
2855
2856 zcu.builtin_modules.deinit(gpa);
2857 zcu.module_roots.deinit(gpa);
2858 for (zcu.import_table.keys()) |file_index| {
2859 zcu.destroyFile(file_index);
2860 }
2861 zcu.import_table.deinit(gpa);
2862 zcu.alive_files.deinit(gpa);
2863
2864 for (zcu.embed_table.keys()) |embed_file| {
2865 embed_file.path.deinit(gpa);
2866 gpa.destroy(embed_file);
2867 }
2868 zcu.embed_table.deinit(gpa);
2869
2870 zcu.local_zir_cache.handle.close(io);
2871 zcu.global_zir_cache.handle.close(io);
2872
2873 for (zcu.failed_analysis.values()) |value| value.destroy(gpa);
2874 for (zcu.failed_codegen.values()) |value| value.destroy(gpa);
2875 for (zcu.failed_types.values()) |value| value.destroy(gpa);
2876 zcu.analysis_in_progress.deinit(gpa);
2877 zcu.failed_analysis.deinit(gpa);
2878 zcu.transitive_failed_analysis.deinit(gpa);
2879 zcu.dependency_loops.deinit(gpa);
2880 zcu.dependency_loop_nodes.deinit(gpa);
2881 zcu.failed_codegen.deinit(gpa);
2882 zcu.failed_types.deinit(gpa);
2883
2884 for (zcu.failed_files.values()) |value| {
2885 if (value) |msg| gpa.free(msg);
2886 }
2887 zcu.failed_files.deinit(gpa);
2888 zcu.failed_imports.deinit(gpa);
2889
2890 for (zcu.failed_exports.values()) |value| {
2891 value.destroy(gpa);
2892 }
2893 zcu.failed_exports.deinit(gpa);
2894
2895 for (zcu.cimport_errors.values()) |*errs| {
2896 errs.deinit(gpa);
2897 }
2898 zcu.cimport_errors.deinit(gpa);
2899
2900 zcu.compile_logs.deinit(gpa);
2901 zcu.compile_log_lines.deinit(gpa);
2902 zcu.free_compile_log_lines.deinit(gpa);
2903
2904 zcu.all_exports.deinit(gpa);
2905 zcu.free_exports.deinit(gpa);
2906 zcu.single_exports.deinit(gpa);
2907 zcu.multi_exports.deinit(gpa);
2908
2909 zcu.potentially_outdated.deinit(gpa);
2910 zcu.outdated.deinit(gpa);
2911 zcu.outdated_ready.funcs.deinit(gpa);
2912 zcu.outdated_ready.other.deinit(gpa);
2913 zcu.retryable_failures.deinit(gpa);
2914
2915 zcu.test_functions.deinit(gpa);
2916
2917 for (zcu.global_assembly.values()) |s| {
2918 gpa.free(s);
2919 }
2920 zcu.global_assembly.deinit(gpa);
2921
2922 zcu.reference_table.deinit(gpa);
2923 zcu.all_references.deinit(gpa);
2924 zcu.free_references.deinit(gpa);
2925
2926 zcu.inline_reference_frames.deinit(gpa);
2927 zcu.free_inline_reference_frames.deinit(gpa);
2928
2929 zcu.type_reference_table.deinit(gpa);
2930 zcu.all_type_references.deinit(gpa);
2931 zcu.free_type_references.deinit(gpa);
2932
2933 if (zcu.resolved_references) |*r| r.deinit(gpa);
2934
2935 if (comp.debugIncremental()) {
2936 zcu.incremental_debug_state.deinit(gpa);
2937 }
2938 }
2939 zcu.intern_pool.deinit(gpa, io);
2940}
2941
2942fn deinitFile(zcu: *Zcu, file_index: Zcu.File.Index) void {
2943 const gpa = zcu.gpa;
2944 const file = zcu.fileByIndex(file_index);
2945 log.debug("deinit File {f}", .{file.path.fmt(zcu.comp)});
2946 file.path.deinit(gpa);
2947 file.unload(gpa);
2948 if (file.prev_zir) |prev_zir| {
2949 prev_zir.deinit(gpa);
2950 gpa.destroy(prev_zir);
2951 }
2952 file.* = undefined;
2953}
2954
2955fn destroyFile(zcu: *Zcu, file_index: Zcu.File.Index) void {
2956 const gpa = zcu.gpa;
2957 const file = zcu.fileByIndex(file_index);
2958 deinitFile(zcu, file_index);
2959 gpa.destroy(file);
2960}
2961
2962pub fn namespacePtr(zcu: *Zcu, index: Namespace.Index) *Namespace {
2963 return zcu.intern_pool.namespacePtr(index);
2964}
2965
2966pub fn namespacePtrUnwrap(zcu: *Zcu, index: Namespace.OptionalIndex) ?*Namespace {
2967 return zcu.namespacePtr(index.unwrap() orelse return null);
2968}
2969
2970// TODO https://github.com/ziglang/zig/issues/8643
2971pub const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
2972pub const HackDataLayout = extern struct {
2973 data: [8]u8 align(@alignOf(Zir.Inst.Data)),
2974 safety_tag: u8,
2975};
2976comptime {
2977 if (data_has_safety_tag) {
2978 assert(@sizeOf(HackDataLayout) == @sizeOf(Zir.Inst.Data));
2979 }
2980}
2981
2982pub fn loadZirCache(gpa: Allocator, io: Io, cache_file: Io.File) !Zir {
2983 var buffer: [2000]u8 = undefined;
2984 var file_reader = cache_file.reader(io, &buffer);
2985 return result: {
2986 const header = file_reader.interface.takeStructPointer(Zir.Header) catch |err| break :result err;
2987 break :result loadZirCacheBody(gpa, header.*, &file_reader.interface);
2988 } catch |err| switch (err) {
2989 error.ReadFailed => return file_reader.err.?,
2990 else => |e| return e,
2991 };
2992}
2993
2994pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_br: *Io.Reader) !Zir {
2995 var instructions: std.MultiArrayList(Zir.Inst) = .{};
2996 errdefer instructions.deinit(gpa);
2997
2998 try instructions.setCapacity(gpa, header.instructions_len);
2999 instructions.len = header.instructions_len;
3000
3001 var zir: Zir = .{
3002 .instructions = instructions.toOwnedSlice(),
3003 .string_bytes = &.{},
3004 .extra = &.{},
3005 };
3006 errdefer zir.deinit(gpa);
3007
3008 zir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
3009 zir.extra = try gpa.alloc(u32, header.extra_len);
3010
3011 const safety_buffer = if (data_has_safety_tag)
3012 try gpa.alloc([8]u8, header.instructions_len)
3013 else
3014 undefined;
3015 defer if (data_has_safety_tag) gpa.free(safety_buffer);
3016
3017 var vecs = [_][]u8{
3018 @ptrCast(zir.instructions.items(.tag)),
3019 if (data_has_safety_tag)
3020 @ptrCast(safety_buffer)
3021 else
3022 @ptrCast(zir.instructions.items(.data)),
3023 zir.string_bytes,
3024 @ptrCast(zir.extra),
3025 };
3026 try cache_br.readVecAll(&vecs);
3027 if (data_has_safety_tag) {
3028 const tags = zir.instructions.items(.tag);
3029 for (zir.instructions.items(.data), 0..) |*data, i| {
3030 const union_tag = Zir.Inst.Tag.data_tags[@backingInt(tags[i])];
3031 const as_struct = @as(*HackDataLayout, @ptrCast(data));
3032 as_struct.* = .{
3033 .safety_tag = @backingInt(union_tag),
3034 .data = safety_buffer[i],
3035 };
3036 }
3037 }
3038 return zir;
3039}
3040
3041pub fn saveZirCache(
3042 gpa: Allocator,
3043 cache_file_writer: *Io.File.Writer,
3044 stat: Io.File.Stat,
3045 zir: Zir,
3046) (Io.File.Writer.Error || Allocator.Error)!void {
3047 const safety_buffer = if (data_has_safety_tag)
3048 try gpa.alloc([8]u8, zir.instructions.len)
3049 else
3050 undefined;
3051 defer if (data_has_safety_tag) gpa.free(safety_buffer);
3052
3053 if (data_has_safety_tag) {
3054 // The `Data` union has a safety tag but in the file format we store it without.
3055 for (zir.instructions.items(.data), 0..) |*data, i| {
3056 const as_struct: *const HackDataLayout = @ptrCast(data);
3057 safety_buffer[i] = as_struct.data;
3058 }
3059 }
3060
3061 const header: Zir.Header = .{
3062 .instructions_len = @intCast(zir.instructions.len),
3063 .string_bytes_len = @intCast(zir.string_bytes.len),
3064 .extra_len = @intCast(zir.extra.len),
3065
3066 .stat_size = stat.size,
3067 .stat_inode = stat.inode,
3068 .stat_mtime = stat.mtime.toNanoseconds(),
3069 };
3070 var vecs = [_][]const u8{
3071 @ptrCast((&header)[0..1]),
3072 @ptrCast(zir.instructions.items(.tag)),
3073 if (data_has_safety_tag)
3074 @ptrCast(safety_buffer)
3075 else
3076 @ptrCast(zir.instructions.items(.data)),
3077 zir.string_bytes,
3078 @ptrCast(zir.extra),
3079 };
3080 cache_file_writer.interface.writeVecAll(&vecs) catch |err| switch (err) {
3081 error.WriteFailed => return cache_file_writer.err.?,
3082 };
3083}
3084
3085pub fn saveZoirCache(cache_file_writer: *Io.File.Writer, stat: Io.File.Stat, zoir: Zoir) Io.File.Writer.Error!void {
3086 const header: Zoir.Header = .{
3087 .nodes_len = @intCast(zoir.nodes.len),
3088 .extra_len = @intCast(zoir.extra.len),
3089 .limbs_len = @intCast(zoir.limbs.len),
3090 .string_bytes_len = @intCast(zoir.string_bytes.len),
3091 .compile_errors_len = @intCast(zoir.compile_errors.len),
3092 .error_notes_len = @intCast(zoir.error_notes.len),
3093
3094 .stat_size = stat.size,
3095 .stat_inode = stat.inode,
3096 .stat_mtime = stat.mtime.toNanoseconds(),
3097 };
3098 var vecs = [_][]const u8{
3099 @ptrCast((&header)[0..1]),
3100 @ptrCast(zoir.nodes.items(.tag)),
3101 @ptrCast(zoir.nodes.items(.data)),
3102 @ptrCast(zoir.nodes.items(.ast_node)),
3103 @ptrCast(zoir.extra),
3104 @ptrCast(zoir.limbs),
3105 zoir.string_bytes,
3106 @ptrCast(zoir.compile_errors),
3107 @ptrCast(zoir.error_notes),
3108 };
3109 cache_file_writer.interface.writeVecAll(&vecs) catch |err| switch (err) {
3110 error.WriteFailed => return cache_file_writer.err.?,
3111 };
3112}
3113
3114pub fn loadZoirCacheBody(gpa: Allocator, header: Zoir.Header, cache_br: *Io.Reader) !Zoir {
3115 var zoir: Zoir = .{
3116 .nodes = .empty,
3117 .extra = &.{},
3118 .limbs = &.{},
3119 .string_bytes = &.{},
3120 .compile_errors = &.{},
3121 .error_notes = &.{},
3122 };
3123 errdefer zoir.deinit(gpa);
3124
3125 zoir.nodes = nodes: {
3126 var nodes: std.MultiArrayList(Zoir.Node.Repr) = .empty;
3127 defer nodes.deinit(gpa);
3128 try nodes.setCapacity(gpa, header.nodes_len);
3129 nodes.len = header.nodes_len;
3130 break :nodes nodes.toOwnedSlice();
3131 };
3132
3133 zoir.extra = try gpa.alloc(u32, header.extra_len);
3134 zoir.limbs = try gpa.alloc(std.math.big.Limb, header.limbs_len);
3135 zoir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
3136
3137 zoir.compile_errors = try gpa.alloc(Zoir.CompileError, header.compile_errors_len);
3138 zoir.error_notes = try gpa.alloc(Zoir.CompileError.Note, header.error_notes_len);
3139
3140 var vecs = [_][]u8{
3141 @ptrCast(zoir.nodes.items(.tag)),
3142 @ptrCast(zoir.nodes.items(.data)),
3143 @ptrCast(zoir.nodes.items(.ast_node)),
3144 @ptrCast(zoir.extra),
3145 @ptrCast(zoir.limbs),
3146 zoir.string_bytes,
3147 @ptrCast(zoir.compile_errors),
3148 @ptrCast(zoir.error_notes),
3149 };
3150 try cache_br.readVecAll(&vecs);
3151 return zoir;
3152}
3153
3154pub fn markDependeeOutdated(
3155 zcu: *Zcu,
3156 /// When we are diffing ZIR and marking things as outdated, we won't yet have marked the dependencies as PO.
3157 /// However, when we discover during analysis that something was outdated, the `Dependee` was already
3158 /// marked as PO, so we need to decrement the PO dep count for each depender.
3159 marked_po: enum { not_marked_po, marked_po },
3160 dependee: InternPool.Dependee,
3161) !void {
3162 const gpa = zcu.comp.gpa;
3163 deps_log.debug("outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
3164 var it = zcu.intern_pool.dependencyIterator(dependee);
3165 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3166 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3167 while (it.next()) |depender| {
3168 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
3169 switch (marked_po) {
3170 .not_marked_po => {},
3171 .marked_po => {
3172 po_dep_count.* -= 1;
3173 deps_log.debug("outdated {f} => already outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
3174 if (po_dep_count.* == 0) {
3175 deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
3176 switch (depender.unwrap()) {
3177 .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}),
3178 else => try zcu.outdated_ready.other.put(gpa, depender, {}),
3179 }
3180 }
3181 },
3182 }
3183 continue;
3184 }
3185 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);
3186 const new_po_dep_count = switch (marked_po) {
3187 .not_marked_po => if (opt_po_entry) |e| e.value else 0,
3188 .marked_po => if (opt_po_entry) |e| e.value - 1 else {
3189 // This `AnalUnit` has already been re-analyzed this update, and registered a dependency
3190 // on this thing, but already has sufficiently up-to-date information. Nothing to do.
3191 continue;
3192 },
3193 };
3194 try zcu.outdated.putNoClobber(
3195 gpa,
3196 depender,
3197 new_po_dep_count,
3198 );
3199 deps_log.debug("outdated {f} => new outdated {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count });
3200 if (new_po_dep_count == 0) {
3201 deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
3202 switch (depender.unwrap()) {
3203 .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}),
3204 else => try zcu.outdated_ready.other.put(gpa, depender, {}),
3205 }
3206 }
3207 // If this is a Decl and was not previously PO, we must recursively
3208 // mark dependencies on its tyval as PO.
3209 if (opt_po_entry == null) {
3210 assert(marked_po == .not_marked_po);
3211 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
3212 }
3213 }
3214
3215 zcu.updateTracyOutdatedPlots();
3216}
3217
3218pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3219 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3220 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3221 try markPoDependeeUpToDateInner(zcu, dependee);
3222 zcu.updateTracyOutdatedPlots();
3223}
3224/// Assumes that `zcu.outdated_lock` is already held exclusively.
3225fn markPoDependeeUpToDateInner(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3226 const gpa = zcu.comp.gpa;
3227 deps_log.debug("up-to-date dependee: {f}", .{zcu.fmtDependee(dependee)});
3228 var it = zcu.intern_pool.dependencyIterator(dependee);
3229 while (it.next()) |depender| {
3230 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
3231 // This depender is already outdated, but it now has one
3232 // less PO dependency!
3233 po_dep_count.* -= 1;
3234 deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
3235 if (po_dep_count.* == 0) {
3236 deps_log.debug("outdated ready: {f}", .{zcu.fmtAnalUnit(depender)});
3237 switch (depender.unwrap()) {
3238 .func => |func| try zcu.outdated_ready.funcs.put(gpa, func, {}),
3239 else => try zcu.outdated_ready.other.put(gpa, depender, {}),
3240 }
3241 }
3242 continue;
3243 }
3244 // This depender is definitely at least PO, because this Decl was just analyzed
3245 // due to being outdated.
3246 const ptr = zcu.potentially_outdated.getPtr(depender) orelse {
3247 // This dependency has been registered during in-progress analysis, but the unit is
3248 // not in `potentially_outdated` because analysis is in-progress. Nothing to do.
3249 continue;
3250 };
3251 if (ptr.* > 1) {
3252 ptr.* -= 1;
3253 deps_log.debug("up-to-date {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* });
3254 continue;
3255 }
3256
3257 deps_log.debug("up-to-date {f} => {f} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) });
3258
3259 // This dependency is no longer PO, i.e. is known to be up-to-date.
3260 assert(zcu.potentially_outdated.swapRemove(depender));
3261 // If this is a Decl, we must recursively mark dependencies on its tyval
3262 // as no longer PO.
3263 switch (depender.unwrap()) {
3264 .@"comptime" => {},
3265 .nav_val => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_val = nav }),
3266 .nav_ty => |nav| try zcu.markPoDependeeUpToDateInner(.{ .nav_ty = nav }),
3267 .type_layout => |ty| try zcu.markPoDependeeUpToDateInner(.{ .type_layout = ty }),
3268 .struct_defaults => |ty| try zcu.markPoDependeeUpToDateInner(.{ .struct_defaults = ty }),
3269 .func => |func| try zcu.markPoDependeeUpToDateInner(.{ .func_ies = func }),
3270 .memoized_state => |stage| try zcu.markPoDependeeUpToDateInner(.{ .memoized_state = stage }),
3271 }
3272 }
3273}
3274
3275/// Given a AnalUnit which is newly outdated or PO, mark all AnalUnits which may
3276/// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES.
3277///
3278/// Assumes that `zcu.outdated_lock` is already held exclusively.
3279fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) Allocator.Error!void {
3280 const gpa = zcu.comp.gpa;
3281 const ip = &zcu.intern_pool;
3282 const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
3283 .@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
3284 .nav_val => |nav| .{ .nav_val = nav },
3285 .nav_ty => |nav| .{ .nav_ty = nav },
3286 .type_layout => |ty| .{ .type_layout = ty },
3287 .struct_defaults => |ty| .{ .struct_defaults = ty },
3288 .func => |func_index| .{ .func_ies = func_index },
3289 .memoized_state => |stage| .{ .memoized_state = stage },
3290 };
3291 deps_log.debug("potentially outdated dependee: {f}", .{zcu.fmtDependee(dependee)});
3292 var it = ip.dependencyIterator(dependee);
3293 while (it.next()) |po| {
3294 if (zcu.outdated.getPtr(po)) |po_dep_count| {
3295 // This dependency is already outdated, but it now has one more PO dependency.
3296 if (po_dep_count.* == 0) {
3297 switch (po.unwrap()) {
3298 .func => |func| _ = zcu.outdated_ready.funcs.swapRemove(func),
3299 else => _ = zcu.outdated_ready.other.swapRemove(po),
3300 }
3301 }
3302 po_dep_count.* += 1;
3303 deps_log.debug("po {f} => {f} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* });
3304 continue;
3305 }
3306 if (zcu.potentially_outdated.getPtr(po)) |n| {
3307 // There is now one more PO dependency.
3308 n.* += 1;
3309 deps_log.debug("po {f} => {f} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* });
3310 continue;
3311 }
3312 try zcu.potentially_outdated.putNoClobber(gpa, po, 1);
3313 deps_log.debug("po {f} => {f} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) });
3314 // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.
3315 try zcu.markTransitiveDependersPotentiallyOutdated(po);
3316 }
3317}
3318
3319/// Selects an outdated `AnalUnit` to analyze next. Called from the main semantic analysis loop when
3320/// there is no work immediately queued. The unit is chosen such that it is unlikely to require any
3321/// recursive analysis (all of its previously-marked dependencies are already up-to-date), because
3322/// recursive analysis can cause over-analysis on incremental updates.
3323pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
3324 // We prioritize functions, because the sooner they get analyzed, the sooner they can be send to
3325 // the codegen backend and linker, which are usually running in parallel (so this can increase
3326 // parallelism).
3327 // TODO: perhaps we should also experiment with *avoiding* functions if the codegen/link queue
3328 // is backed up (for instance due to a very large function). That could help minimize blocking
3329 // on the main thread in `CodegenTaskPool.start` waiting for the linker to catch up.
3330 if (zcu.outdated_ready.funcs.count() > 0) {
3331 const unit: AnalUnit = .wrap(.{ .func = zcu.outdated_ready.funcs.keys()[0] });
3332 log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)});
3333 return unit;
3334 }
3335
3336 if (zcu.outdated_ready.other.count() > 0) {
3337 const unit = zcu.outdated_ready.other.keys()[0];
3338 log.debug("findOutdatedToAnalyze: {f}", .{zcu.fmtAnalUnit(unit)});
3339 return unit;
3340 }
3341
3342 // Usually, getting here means that everything is up-to-date, so there is no more work to do. We
3343 // will see that `zcu.outdated` and `zcu.potentially_outdated` are both empty.
3344 //
3345 // However, if a previous update had a dependency loop compile error, there is a cycle in the
3346 // dependency graph (which is usually acyclic), which can cause a scenario where no unit appears
3347 // to be ready, because they're all waiting for the next in the loop to be up-to-date. In that
3348 // case, we usually have to just bite the bullet and analyze one of them. An exception is if
3349 // `zcu.outdated` is empty but `zcu.potentially_outdated` is non-empty: in that case, the only
3350 // possible situation is a cycle where everything is actually up-to-date, so we can clear out
3351 // `zcu.potentially_outdated` and we are done.
3352
3353 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3354 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3355
3356 if (zcu.outdated.count() == 0) {
3357 // Everything is up-to-date. There could be lingering entries in `zcu.potentially_outdated`
3358 // from a dependency loop on a previous update.
3359 zcu.potentially_outdated.clearRetainingCapacity();
3360 zcu.updateTracyOutdatedPlots();
3361 log.debug("findOutdatedToAnalyze: all up-to-date", .{});
3362 return null;
3363 }
3364
3365 const unit = zcu.outdated.keys()[0];
3366 log.debug("findOutdatedToAnalyze: dependency loop affecting {d} units, selected {f}", .{
3367 zcu.outdated.count(),
3368 zcu.fmtAnalUnit(unit),
3369 });
3370 return unit;
3371}
3372
3373/// During an incremental update, before semantic analysis, call this to flush all values from
3374/// `retryable_failures` and mark them as outdated so they get re-analyzed.
3375pub fn flushRetryableFailures(zcu: *Zcu) !void {
3376 const comp = zcu.comp;
3377 const gpa = comp.gpa;
3378 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(comp.io);
3379 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(comp.io);
3380 for (zcu.retryable_failures.items) |depender| {
3381 if (zcu.outdated.contains(depender)) continue;
3382 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| {
3383 // This AnalUnit was already PO, but we now consider it outdated.
3384 // Any transitive dependencies are already marked PO.
3385 try zcu.outdated.put(gpa, depender, kv.value);
3386 continue;
3387 }
3388 // This AnalUnit was not marked PO, but is now outdated. Mark it as
3389 // such, then recursively mark transitive dependencies as PO.
3390 try zcu.outdated.put(gpa, depender, 0);
3391 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
3392 }
3393 zcu.retryable_failures.clearRetainingCapacity();
3394 zcu.updateTracyOutdatedPlots();
3395}
3396
3397pub fn mapOldZirToNew(
3398 gpa: Allocator,
3399 old_zir: Zir,
3400 new_zir: Zir,
3401 inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
3402) Allocator.Error!void {
3403 // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc.
3404 // Not `declaration`, as this does not create a namespace.
3405 const MatchedZirDecl = struct {
3406 old_inst: Zir.Inst.Index,
3407 new_inst: Zir.Inst.Index,
3408 };
3409 var pending_matched_type_decls: std.ArrayList(MatchedZirDecl) = .empty;
3410 defer pending_matched_type_decls.deinit(gpa);
3411
3412 // Used as temporary buffers for namespace declaration instructions
3413 var old_contents: Zir.DeclContents = .init;
3414 defer old_contents.deinit(gpa);
3415 var new_contents: Zir.DeclContents = .init;
3416 defer new_contents.deinit(gpa);
3417
3418 // Map the main struct inst to start off with.
3419 try pending_matched_type_decls.append(gpa, .{
3420 .old_inst = .main_struct_inst,
3421 .new_inst = .main_struct_inst,
3422 });
3423
3424 while (pending_matched_type_decls.pop()) |match_item| {
3425 // There are some properties of type declarations which cannot change across incremental
3426 // updates. If they have, we need to ignore this mapping. These properties are essentially
3427 // everything passed into `InternPool.getDeclaredStructType` (likewise for unions, enums,
3428 // and opaques).
3429 const old_tag = old_zir.instructions.items(.data)[@backingInt(match_item.old_inst)].extended.opcode;
3430 const new_tag = new_zir.instructions.items(.data)[@backingInt(match_item.new_inst)].extended.opcode;
3431 if (old_tag != new_tag) continue;
3432 switch (old_tag) {
3433 .struct_decl => {
3434 const old = old_zir.getStructDecl(match_item.old_inst);
3435 const new = new_zir.getStructDecl(match_item.new_inst);
3436 if (old.captures.len != new.captures.len) continue;
3437 if (old.field_names.len != new.field_names.len) continue;
3438 if (old.layout != new.layout) continue;
3439 const old_any_field_aligns = old.field_align_body_lens != null;
3440 const old_any_field_defaults = old.field_default_body_lens != null;
3441 const old_any_comptime_fields = old.field_comptime_bits != null;
3442 const old_explicit_backing_int = old.backing_int_type_body != null;
3443 const new_any_field_aligns = new.field_align_body_lens != null;
3444 const new_any_field_defaults = new.field_default_body_lens != null;
3445 const new_any_comptime_fields = new.field_comptime_bits != null;
3446 const new_explicit_backing_int = new.backing_int_type_body != null;
3447 if (old_any_field_aligns != new_any_field_aligns) continue;
3448 if (old_any_field_defaults != new_any_field_defaults) continue;
3449 if (old_any_comptime_fields != new_any_comptime_fields) continue;
3450 if (old_explicit_backing_int != new_explicit_backing_int) continue;
3451 },
3452 .union_decl => {
3453 const old = old_zir.getUnionDecl(match_item.old_inst);
3454 const new = new_zir.getUnionDecl(match_item.new_inst);
3455 if (old.captures.len != new.captures.len) continue;
3456 if (old.field_names.len != new.field_names.len) continue;
3457 if (old.kind != new.kind) continue;
3458 const old_any_field_aligns = old.field_align_body_lens != null;
3459 const new_any_field_aligns = new.field_align_body_lens != null;
3460 if (old_any_field_aligns != new_any_field_aligns) continue;
3461 },
3462 .enum_decl => {
3463 const old = old_zir.getEnumDecl(match_item.old_inst);
3464 const new = new_zir.getEnumDecl(match_item.new_inst);
3465 if (old.captures.len != new.captures.len) continue;
3466 if (old.field_names.len != new.field_names.len) continue;
3467 if (old.nonexhaustive != new.nonexhaustive) continue;
3468 const old_explicit_tag_type = old.tag_type_body != null;
3469 const new_explicit_tag_type = new.tag_type_body != null;
3470 if (old_explicit_tag_type != new_explicit_tag_type) continue;
3471 },
3472 .opaque_decl => {
3473 const old = old_zir.getOpaqueDecl(match_item.old_inst);
3474 const new = new_zir.getOpaqueDecl(match_item.new_inst);
3475 if (old.captures.len != new.captures.len) continue;
3476 },
3477 else => unreachable,
3478 }
3479
3480 // Match the container declaration itself
3481 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);
3482
3483 {
3484 // First, map the fields...
3485 try old_zir.findTrackableFields(gpa, &old_contents, match_item.old_inst);
3486 try new_zir.findTrackableFields(gpa, &new_contents, match_item.new_inst);
3487
3488 // This isn't a `.declaration`, so we shouldn't see a function declaration.
3489 assert(old_contents.func_decl == null);
3490 assert(new_contents.func_decl == null);
3491
3492 // We don't have any smart way of matching up these instructions, so we correlate them based on source order
3493 // in their respective arrays.
3494
3495 const num_type_decls = @min(old_contents.type_decls.items.len, new_contents.type_decls.items.len);
3496 try pending_matched_type_decls.ensureUnusedCapacity(gpa, @intCast(num_type_decls));
3497 for (
3498 old_contents.type_decls.items[0..num_type_decls],
3499 new_contents.type_decls.items[0..num_type_decls],
3500 ) |old_inst, new_inst| {
3501 pending_matched_type_decls.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3502 }
3503
3504 const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
3505 try inst_map.ensureUnusedCapacity(gpa, @intCast(num_other));
3506 for (
3507 old_contents.other.items[0..num_other],
3508 new_contents.other.items[0..num_other],
3509 ) |old_inst, new_inst| {
3510 // These instructions don't have declarations, so we just modify `inst_map` directly.
3511 inst_map.putAssumeCapacity(old_inst, new_inst);
3512 }
3513 }
3514
3515 // Maps decl name to `declaration` instruction.
3516 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;
3517 defer named_decls.deinit(gpa);
3518 // Maps test name to `declaration` instruction.
3519 var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;
3520 defer named_tests.deinit(gpa);
3521 // Maps test name to `declaration` instruction.
3522 var named_decltests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .empty;
3523 defer named_decltests.deinit(gpa);
3524 // All unnamed tests, in order, for a best-effort match.
3525 var unnamed_tests: std.ArrayList(Zir.Inst.Index) = .empty;
3526 defer unnamed_tests.deinit(gpa);
3527 // All comptime declarations, in order, for a best-effort match.
3528 var comptime_decls: std.ArrayList(Zir.Inst.Index) = .empty;
3529 defer comptime_decls.deinit(gpa);
3530
3531 for (old_zir.typeDecls(match_item.old_inst)) |old_decl_inst| {
3532 const old_decl = old_zir.getDeclaration(old_decl_inst);
3533 switch (old_decl.kind) {
3534 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
3535 .unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
3536 .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
3537 .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
3538 .@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
3539 }
3540 }
3541
3542 var unnamed_test_idx: u32 = 0;
3543 var comptime_decl_idx: u32 = 0;
3544
3545 for (new_zir.typeDecls(match_item.new_inst)) |new_decl_inst| {
3546 const new_decl = new_zir.getDeclaration(new_decl_inst);
3547 // Attempt to match this to a declaration in the old ZIR:
3548 // * For named declarations (`const`/`var`/`fn`), we match based on name.
3549 // * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name.
3550 // * For unnamed tests, we match based on order.
3551 // * For comptime blocks, we match based on order.
3552 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
3553 const old_decl_inst = switch (new_decl.kind) {
3554 .@"comptime" => inst: {
3555 if (comptime_decl_idx == comptime_decls.items.len) continue;
3556 defer comptime_decl_idx += 1;
3557 break :inst comptime_decls.items[comptime_decl_idx];
3558 },
3559 .unnamed_test => inst: {
3560 if (unnamed_test_idx == unnamed_tests.items.len) continue;
3561 defer unnamed_test_idx += 1;
3562 break :inst unnamed_tests.items[unnamed_test_idx];
3563 },
3564 .@"test" => inst: {
3565 const name = new_zir.nullTerminatedString(new_decl.name);
3566 break :inst named_tests.get(name) orelse continue;
3567 },
3568 .decltest => inst: {
3569 const name = new_zir.nullTerminatedString(new_decl.name);
3570 break :inst named_decltests.get(name) orelse continue;
3571 },
3572 .@"const", .@"var" => inst: {
3573 const name = new_zir.nullTerminatedString(new_decl.name);
3574 break :inst named_decls.get(name) orelse continue;
3575 },
3576 };
3577
3578 // Match the `declaration` instruction
3579 try inst_map.put(gpa, old_decl_inst, new_decl_inst);
3580
3581 // Find trackable instructions within this declaration
3582 try old_zir.findTrackable(gpa, &old_contents, old_decl_inst);
3583 try new_zir.findTrackable(gpa, &new_contents, new_decl_inst);
3584
3585 // We don't have any smart way of matching up these instructions, so we correlate them based on source order
3586 // in their respective arrays.
3587
3588 const num_type_decls = @min(old_contents.type_decls.items.len, new_contents.type_decls.items.len);
3589 try pending_matched_type_decls.ensureUnusedCapacity(gpa, @intCast(num_type_decls));
3590 for (
3591 old_contents.type_decls.items[0..num_type_decls],
3592 new_contents.type_decls.items[0..num_type_decls],
3593 ) |old_inst, new_inst| {
3594 pending_matched_type_decls.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3595 }
3596
3597 const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
3598 try inst_map.ensureUnusedCapacity(gpa, @intCast(num_other));
3599 for (
3600 old_contents.other.items[0..num_other],
3601 new_contents.other.items[0..num_other],
3602 ) |old_inst, new_inst| {
3603 // These instructions don't have declarations, so we just modify `inst_map` directly.
3604 inst_map.putAssumeCapacity(old_inst, new_inst);
3605 }
3606
3607 if (old_contents.func_decl) |old_func_inst| {
3608 if (new_contents.func_decl) |new_func_inst| {
3609 // There are no declarations on a function either, so again, we just directly add it to `inst_map`.
3610 try inst_map.put(gpa, old_func_inst, new_func_inst);
3611 }
3612 }
3613 }
3614 }
3615}
3616
3617/// Ensure this function's body is or will be analyzed and emitted. This should
3618/// be called whenever a potential runtime call of a function is seen.
3619///
3620/// The caller is responsible for ensuring the function decl itself is already
3621/// analyzed, and for ensuring it can exist at runtime (see
3622/// `Type.fnHasRuntimeBitsSema`). This function does *not* guarantee that the body
3623/// will be analyzed when it returns: for that, see `PerThread.ensureFuncBodyUpToDate`.
3624pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func: InternPool.Index) !void {
3625 const comp = zcu.comp;
3626 const gpa = comp.gpa;
3627 const io = comp.io;
3628 const ip = &zcu.intern_pool;
3629 assert(func == ip.unwrapCoercedFunc(func)); // analyze the body of the original function, not a coerced one
3630 if (ip.setWantRuntimeFnAnalysis(io, func)) {
3631 // This is the first reference to this function, so we must ensure it will be analyzed.
3632 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3633 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3634 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
3635 try zcu.outdated_ready.funcs.ensureUnusedCapacity(gpa, 1);
3636 zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .func = func }), 0);
3637 zcu.outdated_ready.funcs.putAssumeCapacityNoClobber(func, {});
3638 zcu.updateTracyOutdatedPlots();
3639 }
3640}
3641
3642pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav: InternPool.Nav.Index) !void {
3643 const comp = zcu.comp;
3644 const gpa = comp.gpa;
3645 const io = comp.io;
3646 const ip = &zcu.intern_pool;
3647 if (ip.setWantNavAnalysis(io, nav)) {
3648 // This is the first reference to this function, so we must ensure it will be analyzed.
3649 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(zcu.comp.io);
3650 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(zcu.comp.io);
3651 try zcu.outdated.ensureUnusedCapacity(gpa, 2);
3652 try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 2);
3653 zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), 0);
3654 zcu.outdated.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), 0);
3655 zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .nav_val = nav }), {});
3656 zcu.outdated_ready.other.putAssumeCapacityNoClobber(.wrap(.{ .nav_ty = nav }), {});
3657 zcu.updateTracyOutdatedPlots();
3658 }
3659}
3660
3661/// Called when an `InternPool.ComptimeUnit` is first created to mark it as outdated so that it will
3662/// be semantically analyzed.
3663pub fn queueComptimeUnitAnalysis(zcu: *Zcu, cu: InternPool.ComptimeUnit.Id) Allocator.Error!void {
3664 const comp = zcu.comp;
3665 const gpa = comp.gpa;
3666 const io = comp.io;
3667 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
3668 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io);
3669 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io);
3670 try zcu.outdated.ensureUnusedCapacity(gpa, 1);
3671 try zcu.outdated_ready.other.ensureUnusedCapacity(gpa, 1);
3672 zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
3673 zcu.outdated_ready.other.putAssumeCapacityNoClobber(unit, {});
3674 zcu.updateTracyOutdatedPlots();
3675}
3676
3677/// If `unit` was marked as outdated or porentially outdated, clears that status and returns `true`.
3678/// Otherwise, returns `false`.
3679pub fn clearOutdatedState(zcu: *Zcu, unit: AnalUnit) bool {
3680 const io = zcu.comp.io;
3681 if (std.debug.runtime_safety) zcu.outdated_lock.lockUncancelable(io);
3682 defer if (std.debug.runtime_safety) zcu.outdated_lock.unlock(io);
3683 if (zcu.outdated.fetchSwapRemove(unit)) |kv| {
3684 const was_ready = switch (unit.unwrap()) {
3685 .func => |func| zcu.outdated_ready.funcs.swapRemove(func),
3686 else => zcu.outdated_ready.other.swapRemove(unit),
3687 };
3688 if (kv.value == 0) {
3689 assert(was_ready);
3690 } else {
3691 assert(!was_ready);
3692 }
3693 zcu.updateTracyOutdatedPlots();
3694 return true;
3695 } else if (zcu.potentially_outdated.swapRemove(unit)) {
3696 zcu.updateTracyOutdatedPlots();
3697 return true;
3698 } else {
3699 return false;
3700 }
3701}
3702
3703/// This function takes a `*const Zcu` and `@constCast`s it so that it can be called from functions
3704/// in `Type` which otherwise do not modify the `Zcu`.
3705pub fn assertUpToDate(zcu: *const Zcu, unit: AnalUnit) void {
3706 if (!std.debug.runtime_safety) return;
3707
3708 const io = zcu.comp.io;
3709
3710 @constCast(zcu).outdated_lock.lockSharedUncancelable(io);
3711 defer @constCast(zcu).outdated_lock.unlockShared(io);
3712
3713 assert(!zcu.outdated.contains(unit));
3714 assert(!zcu.potentially_outdated.contains(unit));
3715}
3716
3717pub const ImportResult = struct {
3718 /// Whether `file` has been newly created; in other words, whether this is the first import of
3719 /// this file. This should only be `true` when importing files during AstGen. After that, all
3720 /// files should have already been discovered.
3721 is_new: bool,
3722
3723 /// `file.mod` is not populated by this function, so if `is_new`, then it is `undefined`.
3724 file: *Zcu.File,
3725 file_index: File.Index,
3726
3727 /// If this import was a simple file path, this is `null`; the imported file should exist within
3728 /// the importer's module. Otherwise, it's the module which the import resolved to. This module
3729 /// could match the module of `cur_file`, since a module can depend on itself.
3730 module: ?*Module,
3731};
3732
3733/// Prepares `unit` for re-analysis by clearing all of the following state:
3734/// * Compile errors associated with `unit`
3735/// * Compile logs associated with `unit`
3736/// * Exports performed by `unit`
3737/// * Dependencies from `unit` on other things
3738/// * References from `unit` to other units
3739/// Delete all references in `reference_table` which are caused by `unit`, and all dependencies it
3740/// has. Called in preparation for re-analysis, which will recreate references and dependencies.
3741/// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated.
3742pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void {
3743 const gpa = zcu.comp.gpa;
3744
3745 if (!dev.env.supports(.incremental)) {
3746 // This is the first time `unit` is being analyzed, so there is no stale data to clear.
3747 return;
3748 }
3749
3750 // Compile errors
3751 if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| {
3752 kv.value.destroy(gpa);
3753 } else if (zcu.dependency_loop_nodes.swapRemove(unit)) {
3754 _ = zcu.dependency_loops.swapRemove(unit);
3755 _ = zcu.transitive_failed_analysis.swapRemove(unit);
3756 } else {
3757 _ = zcu.transitive_failed_analysis.swapRemove(unit);
3758 }
3759
3760 // Compile logs
3761 if (zcu.compile_logs.fetchSwapRemove(unit)) |kv| {
3762 var opt_line_idx = kv.value.first_line.toOptional();
3763 while (opt_line_idx.unwrap()) |line_idx| {
3764 zcu.free_compile_log_lines.append(gpa, line_idx) catch {
3765 // This space will be reused eventually, so we need not propagate this error.
3766 // Just leak it for now, and let GC reclaim it later on.
3767 break;
3768 };
3769 opt_line_idx = line_idx.get(zcu).next;
3770 }
3771 }
3772
3773 // Exports
3774 exports: {
3775 const base: u32, const len: u32 = index: {
3776 if (zcu.single_exports.fetchSwapRemove(unit)) |kv| {
3777 break :index .{ @backingInt(kv.value), 1 };
3778 }
3779 if (zcu.multi_exports.fetchSwapRemove(unit)) |kv| {
3780 break :index .{ kv.value.index, kv.value.len };
3781 }
3782 break :exports;
3783 };
3784 for (base..base + len) |exp_index_usize| {
3785 const exp_index: Export.Index = @fromBackingInt(@intCast(exp_index_usize));
3786 if (zcu.failed_exports.fetchSwapRemove(exp_index)) |failed_kv| {
3787 failed_kv.value.destroy(gpa);
3788 }
3789 }
3790 zcu.free_exports.ensureUnusedCapacity(gpa, len) catch {
3791 // This space will be reused eventually, so we need not propagate this error.
3792 // Just leak it for now, and let GC reclaim it later on.
3793 break :exports;
3794 };
3795 for (base..base + len) |exp_index| {
3796 zcu.free_exports.appendAssumeCapacity(@fromBackingInt(@intCast(exp_index)));
3797 }
3798 }
3799
3800 // Dependencies
3801 zcu.intern_pool.removeDependenciesForDepender(gpa, unit);
3802
3803 // References
3804 zcu.clearCachedResolvedReferences();
3805 unit_refs: {
3806 const kv = zcu.reference_table.fetchSwapRemove(unit) orelse break :unit_refs;
3807 var idx = kv.value;
3808
3809 while (idx != std.math.maxInt(u32)) {
3810 const ref = zcu.all_references.items[idx];
3811 zcu.free_references.append(gpa, idx) catch {
3812 // This space will be reused eventually, so we need not propagate this error.
3813 // Just leak it for now, and let GC reclaim it later on.
3814 break :unit_refs;
3815 };
3816 idx = ref.next;
3817
3818 var opt_inline_frame = ref.inline_frame;
3819 while (opt_inline_frame.unwrap()) |inline_frame| {
3820 // The same inline frame could be used multiple times by one unit. We need to
3821 // detect this case to avoid adding it to `free_inline_reference_frames` more
3822 // than once. We do that by setting `parent` to itself as a marker.
3823 if (inline_frame.ptr(zcu).parent == inline_frame.toOptional()) break;
3824 zcu.free_inline_reference_frames.append(gpa, inline_frame) catch {
3825 // This space will be reused eventually, so we need not propagate this error.
3826 // Just leak it for now, and let GC reclaim it later on.
3827 break :unit_refs;
3828 };
3829 opt_inline_frame = inline_frame.ptr(zcu).parent;
3830 inline_frame.ptr(zcu).parent = inline_frame.toOptional(); // signal to code above
3831 }
3832 }
3833 }
3834 type_refs: {
3835 const kv = zcu.type_reference_table.fetchSwapRemove(unit) orelse break :type_refs;
3836 var idx = kv.value;
3837
3838 while (idx != std.math.maxInt(u32)) {
3839 zcu.free_type_references.append(gpa, idx) catch {
3840 // This space will be reused eventually, so we need not propagate this error.
3841 // Just leak it for now, and let GC reclaim it later on.
3842 break :type_refs;
3843 };
3844 idx = zcu.all_type_references.items[idx].next;
3845 }
3846 }
3847}
3848
3849pub fn addInlineReferenceFrame(zcu: *Zcu, frame: InlineReferenceFrame) Allocator.Error!Zcu.InlineReferenceFrame.Index {
3850 const frame_idx: InlineReferenceFrame.Index = zcu.free_inline_reference_frames.pop() orelse idx: {
3851 _ = try zcu.inline_reference_frames.addOne(zcu.gpa);
3852 break :idx @fromBackingInt(@intCast(zcu.inline_reference_frames.items.len - 1));
3853 };
3854 frame_idx.ptr(zcu).* = frame;
3855 return frame_idx;
3856}
3857
3858pub fn addUnitReference(
3859 zcu: *Zcu,
3860 src_unit: AnalUnit,
3861 referenced_unit: AnalUnit,
3862 ref_src: LazySrcLoc,
3863 inline_frame: InlineReferenceFrame.Index.Optional,
3864) Allocator.Error!void {
3865 const gpa = zcu.gpa;
3866
3867 zcu.clearCachedResolvedReferences();
3868
3869 try zcu.reference_table.ensureUnusedCapacity(gpa, 1);
3870
3871 const ref_idx = zcu.free_references.pop() orelse idx: {
3872 _ = try zcu.all_references.addOne(gpa);
3873 break :idx zcu.all_references.items.len - 1;
3874 };
3875
3876 errdefer comptime unreachable;
3877
3878 const gop = zcu.reference_table.getOrPutAssumeCapacity(src_unit);
3879
3880 zcu.all_references.items[ref_idx] = .{
3881 .referenced = referenced_unit,
3882 .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32),
3883 .src = ref_src,
3884 .inline_frame = inline_frame,
3885 };
3886
3887 gop.value_ptr.* = @intCast(ref_idx);
3888}
3889
3890pub fn addTypeReference(zcu: *Zcu, src_unit: AnalUnit, referenced_type: InternPool.Index, ref_src: LazySrcLoc) Allocator.Error!void {
3891 const gpa = zcu.gpa;
3892
3893 zcu.clearCachedResolvedReferences();
3894
3895 try zcu.type_reference_table.ensureUnusedCapacity(gpa, 1);
3896
3897 const ref_idx = zcu.free_type_references.pop() orelse idx: {
3898 _ = try zcu.all_type_references.addOne(gpa);
3899 break :idx zcu.all_type_references.items.len - 1;
3900 };
3901
3902 errdefer comptime unreachable;
3903
3904 const gop = zcu.type_reference_table.getOrPutAssumeCapacity(src_unit);
3905
3906 zcu.all_type_references.items[ref_idx] = .{
3907 .referenced = referenced_type,
3908 .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32),
3909 .src = ref_src,
3910 };
3911
3912 gop.value_ptr.* = @intCast(ref_idx);
3913}
3914
3915fn clearCachedResolvedReferences(zcu: *Zcu) void {
3916 if (zcu.resolved_references) |*r| r.deinit(zcu.gpa);
3917 zcu.resolved_references = null;
3918}
3919
3920pub fn errorSetBits(zcu: *const Zcu) u16 {
3921 const target = zcu.getTarget();
3922
3923 if (zcu.error_limit == 0) return 0;
3924 if (target.cpu.arch.isSpirV()) {
3925 // As expected by https://github.com/Snektron/zig-spirv-test-executor
3926 if (zcu.comp.config.is_test) return 32;
3927 }
3928
3929 return @as(u16, std.math.log2_int(ErrorInt, zcu.error_limit)) + 1;
3930}
3931
3932pub fn errNote(
3933 zcu: *Zcu,
3934 src_loc: LazySrcLoc,
3935 parent: *ErrorMsg,
3936 comptime format: []const u8,
3937 args: anytype,
3938) error{OutOfMemory}!void {
3939 const msg = try std.fmt.allocPrint(zcu.gpa, format, args);
3940 errdefer zcu.gpa.free(msg);
3941
3942 parent.notes = try zcu.gpa.realloc(parent.notes, parent.notes.len + 1);
3943 parent.notes[parent.notes.len - 1] = .{
3944 .src_loc = src_loc,
3945 .msg = msg,
3946 };
3947}
3948
3949/// Deprecated. There is no global target for a Zig Compilation Unit. Instead,
3950/// look up the target based on the Module that contains the source code being
3951/// analyzed.
3952pub fn getTarget(zcu: *const Zcu) *const Target {
3953 return &zcu.root_mod.resolved_target.result;
3954}
3955
3956pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {
3957 const gpa = zcu.gpa;
3958 const gop = try zcu.global_assembly.getOrPut(gpa, unit);
3959 if (gop.found_existing) {
3960 const new_value = try std.fmt.allocPrint(gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
3961 gpa.free(gop.value_ptr.*);
3962 gop.value_ptr.* = new_value;
3963 } else {
3964 gop.value_ptr.* = try gpa.dupe(u8, source);
3965 }
3966}
3967
3968pub const Feature = enum {
3969 /// When this feature is enabled, Sema will emit calls to
3970 /// `std.lang.panic` functions for things like safety checks and
3971 /// unreachables. Otherwise traps will be emitted.
3972 panic_fn,
3973 /// When this feature is enabled, Sema will insert tracer functions for gathering a stack
3974 /// trace for error returns.
3975 error_return_trace,
3976 /// When this feature is enabled, Sema will emit the `is_named_enum_value` AIR instructions
3977 /// and use it to check for corrupt switches. Backends currently need to implement their own
3978 /// logic to determine whether an enum value is in the set of named values.
3979 is_named_enum_value,
3980 error_set_has_value,
3981 field_reordering,
3982 /// In theory, backends are supposed to work like this:
3983 ///
3984 /// * The AIR emitted by `Sema` is converted into MIR by `codegen.generateFunction`. This pass
3985 /// is "pure", in that it does not depend on or modify any external mutable state.
3986 ///
3987 /// * That MIR is sent to the linker, which calls `codegen.emitFunction` to convert the MIR to
3988 /// finalized machine code. This process is permitted to query and modify linker state.
3989 ///
3990 /// * The linker stores the resulting machine code in the binary as needed.
3991 ///
3992 /// The first stage described above can run in parallel to the rest of the compiler, and even to
3993 /// other code generation work; we can run as many codegen threads as we want in parallel because
3994 /// of the fact that this pass is pure. Emit and link must be single-threaded, but are generally
3995 /// very fast, so that isn't a problem.
3996 ///
3997 /// Unfortunately, some code generation implementations currently query and/or mutate linker state
3998 /// or even (in the case of the LLVM backend) semantic analysis state. Such backends cannot be run
3999 /// in parallel with each other, with linking, or (potentially) with semantic analysis.
4000 ///
4001 /// Additionally, some backends continue to need the AIR in the "emit" stage, despite this pass
4002 /// operating on MIR. This complicates memory management under the threading model above.
4003 ///
4004 /// These are both **bugs** in backend implementations, left over from legacy code. However, they
4005 /// are difficult to fix. So, this `Feature` currently guards correct threading of code generation:
4006 ///
4007 /// * With this feature enabled, the backend is threaded as described above. The "emit" stage does
4008 /// not have access to AIR (it will be `undefined`; see `codegen.emitFunction`).
4009 ///
4010 /// * With this feature disabled, semantic analysis, code generation, and linking all occur on the
4011 /// same thread, and the "emit" stage has access to AIR.
4012 separate_thread,
4013};
4014
4015pub fn backendSupportsFeature(zcu: *const Zcu, comptime feature: Feature) bool {
4016 const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm);
4017 return target_util.backendSupportsFeature(backend, feature);
4018}
4019
4020pub const AtomicPtrAlignmentError = error{
4021 FloatTooBig,
4022 IntTooBig,
4023 BadType,
4024 OutOfMemory,
4025};
4026
4027pub const AtomicPtrAlignmentDiagnostics = struct {
4028 bits: u16 = undefined,
4029 max_bits: u16 = undefined,
4030};
4031
4032/// Returns the alignment required for the target to perform atomic operations on type `ty` (that
4033/// is, the required align attribute on the pointer). If the ABI alignment of `ty` is sufficient,
4034/// returns `.none`.
4035// TODO this function does not take into account CPU features, which can affect
4036// this value. Audit this!
4037pub fn atomicPtrAlignment(
4038 zcu: *Zcu,
4039 ty: Type,
4040 diags: *AtomicPtrAlignmentDiagnostics,
4041) AtomicPtrAlignmentError!Alignment {
4042 const target = zcu.getTarget();
4043 const max_atomic_bits: u16 = switch (target.cpu.arch) {
4044 .ez80,
4045 .spork8,
4046 => 8,
4047
4048 .aarch64,
4049 .aarch64_be,
4050 => 128,
4051
4052 .mips64,
4053 .mips64el,
4054 => 64, // N32 should be 64, not 32.
4055
4056 .x86_64 => if (target.cpu.has(.x86, .cx16)) 128 else 64, // x32 should be 64 or 128, not 32.
4057
4058 else => target.ptrBitWidth(),
4059 };
4060
4061 if (ty.toIntern() == .bool_type) return .none;
4062 if (ty.isRuntimeFloat()) {
4063 const bit_count = ty.floatBits(target);
4064 if (bit_count > max_atomic_bits) {
4065 diags.* = .{
4066 .bits = bit_count,
4067 .max_bits = max_atomic_bits,
4068 };
4069 return error.FloatTooBig;
4070 }
4071 return .none;
4072 }
4073 if (switch (ty.zigTypeTag(zcu)) {
4074 .int, .@"enum" => true,
4075 .@"struct" => ty.containerLayout(zcu) == .@"packed",
4076 else => false,
4077 }) {
4078 assert(ty.isAbiInt(zcu));
4079 const bit_count = ty.intInfo(zcu).bits;
4080 if (bit_count > max_atomic_bits) {
4081 diags.* = .{
4082 .bits = bit_count,
4083 .max_bits = max_atomic_bits,
4084 };
4085 return error.IntTooBig;
4086 }
4087 return .none;
4088 }
4089 if (ty.isPtrAtRuntime(zcu)) return .none;
4090 return error.BadType;
4091}
4092
4093/// Returns null if `ty` is not a struct.
4094pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
4095 if (ty.ip_index == .none) return null;
4096 const ip = &zcu.intern_pool;
4097 return switch (ip.indexToKey(ty.ip_index)) {
4098 .struct_type => ip.loadStructType(ty.ip_index),
4099 else => null,
4100 };
4101}
4102
4103pub fn typeToPackedStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
4104 const s = zcu.typeToStruct(ty) orelse return null;
4105 if (s.layout != .@"packed") return null;
4106 return s;
4107}
4108
4109/// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets
4110/// into the packed struct InternPool data rather than computing this on the
4111/// fly, however it was found to perform worse when measured on real world
4112/// projects.
4113pub fn structPackedFieldBitOffset(
4114 zcu: *Zcu,
4115 struct_type: InternPool.LoadedStructType,
4116 field_index: u32,
4117) u16 {
4118 const ip = &zcu.intern_pool;
4119 assert(struct_type.layout == .@"packed");
4120 var bit_sum: u64 = 0;
4121 for (0..struct_type.field_types.len) |i| {
4122 if (i == field_index) {
4123 return @intCast(bit_sum);
4124 }
4125 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
4126 bit_sum += field_ty.bitSize(zcu);
4127 }
4128 unreachable; // index out of bounds
4129}
4130
4131pub fn typeToUnion(zcu: *const Zcu, ty: Type) ?InternPool.LoadedUnionType {
4132 if (ty.ip_index == .none) return null;
4133 const ip = &zcu.intern_pool;
4134 return switch (ip.indexToKey(ty.ip_index)) {
4135 .union_type => ip.loadUnionType(ty.ip_index),
4136 else => null,
4137 };
4138}
4139
4140pub fn typeToFunc(zcu: *const Zcu, ty: Type) ?InternPool.Key.FuncType {
4141 if (ty.ip_index == .none) return null;
4142 return zcu.intern_pool.indexToFuncType(ty.toIntern());
4143}
4144
4145pub fn iesFuncIndex(zcu: *const Zcu, ies_index: InternPool.Index) InternPool.Index {
4146 return zcu.intern_pool.iesFuncIndex(ies_index);
4147}
4148
4149pub fn funcInfo(zcu: *const Zcu, func_index: InternPool.Index) InternPool.Key.Func {
4150 return zcu.intern_pool.toFunc(func_index);
4151}
4152
4153pub const UnionLayout = struct {
4154 abi_size: u64,
4155 abi_align: Alignment,
4156 most_aligned_field: u32,
4157 most_aligned_field_size: u64,
4158 biggest_field: u32,
4159 payload_size: u64,
4160 payload_align: Alignment,
4161 tag_align: Alignment,
4162 tag_size: u64,
4163 padding: u32,
4164
4165 pub fn tagOffset(layout: UnionLayout) u64 {
4166 return if (layout.tag_align.compare(.lt, layout.payload_align)) layout.payload_size else 0;
4167 }
4168
4169 pub fn payloadOffset(layout: UnionLayout) u64 {
4170 return if (layout.tag_align.compare(.lt, layout.payload_align)) 0 else layout.tag_size;
4171 }
4172};
4173
4174/// Returns the index of the active field, given the current tag value
4175pub fn unionTagFieldIndex(zcu: *const Zcu, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
4176 const ip = &zcu.intern_pool;
4177 if (enum_tag.toIntern() == .none) return null;
4178 const enum_tag_key = ip.indexToKey(enum_tag.toIntern()).enum_tag;
4179 assert(enum_tag_key.ty == loaded_union.enum_tag_type);
4180 const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type);
4181 return loaded_enum.tagValueIndex(ip, enum_tag_key.int);
4182}
4183
4184pub const ResolvedReference = struct {
4185 referencer: AnalUnit,
4186 /// If `inline_frame` is not `.none`, this is the *deepest* source location in the chain of
4187 /// inline calls. For source locations further up the inline call stack, consult `inline_frame`.
4188 src: LazySrcLoc,
4189 inline_frame: InlineReferenceFrame.Index.Optional,
4190};
4191
4192/// Returns a mapping from an `AnalUnit` to where it is referenced.
4193/// If the value is `null`, the `AnalUnit` is a root of analysis.
4194/// If an `AnalUnit` is not in the returned map, it is unreferenced.
4195/// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller.
4196/// This hashmap is cached, so repeated calls to this function are cheap.
4197pub fn resolveReferences(zcu: *Zcu) Allocator.Error!*const std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) {
4198 if (zcu.resolved_references == null) {
4199 zcu.resolved_references = try zcu.resolveReferencesInner();
4200 }
4201 return &zcu.resolved_references.?;
4202}
4203fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) {
4204 const trace = tracy.trace(@src());
4205 defer trace.end();
4206
4207 const gpa = zcu.gpa;
4208 const comp = zcu.comp;
4209 const ip = &zcu.intern_pool;
4210
4211 var units: std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) = .empty;
4212 var types: std.array_hash_map.Auto(InternPool.Index, ?ResolvedReference) = .empty;
4213 defer {
4214 units.deinit(gpa);
4215 types.deinit(gpa);
4216 }
4217
4218 // This is not a sufficient size, but an approximate lower bound.
4219 try units.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));
4220
4221 try types.ensureTotalCapacity(gpa, zcu.analysis_roots_len);
4222 for (zcu.analysisRoots()) |mod| {
4223 const file = zcu.module_roots.get(mod).?.unwrap() orelse continue;
4224 const root_ty = zcu.fileRootType(file);
4225 if (root_ty == .none) continue;
4226 types.putAssumeCapacityNoClobber(root_ty, null);
4227 }
4228
4229 var unit_idx: usize = 0;
4230 var type_idx: usize = 0;
4231 while (true) {
4232 if (type_idx < types.count()) {
4233 const ty = types.keys()[type_idx];
4234 const referencer = types.values()[type_idx];
4235 type_idx += 1;
4236
4237 refs_log.debug("handle type '{f}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
4238
4239 // Queue any decls within this type which would be automatically analyzed.
4240 // Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
4241 const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?;
4242 for (zcu.namespacePtr(ns).comptime_decls.items) |cu| {
4243 // `comptime` decls are always analyzed.
4244 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
4245 const gop = try units.getOrPut(gpa, unit);
4246 if (!gop.found_existing) {
4247 refs_log.debug("type '{f}': ref comptime %{}", .{
4248 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
4249 @backingInt(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue),
4250 });
4251 gop.value_ptr.* = referencer;
4252 }
4253 }
4254 for (zcu.namespacePtr(ns).test_decls.items) |nav_id| {
4255 const nav = ip.getNav(nav_id);
4256 // `test` declarations are analyzed depending on the test filter.
4257 const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
4258 const file = zcu.fileByIndex(inst_info.file);
4259 const decl = file.zir.?.getDeclaration(inst_info.inst);
4260
4261 if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
4262
4263 const want_analysis = switch (decl.kind) {
4264 .@"const", .@"var" => unreachable,
4265 .@"comptime" => unreachable,
4266 .unnamed_test => true,
4267 .@"test", .decltest => a: {
4268 const fqn_slice = nav.fqn.toSlice(ip);
4269 if (comp.test_filters.len > 0) {
4270 for (comp.test_filters) |test_filter| {
4271 if (std.mem.find(u8, fqn_slice, test_filter) != null) break;
4272 } else break :a false;
4273 }
4274 break :a true;
4275 },
4276 };
4277 if (want_analysis) {
4278 {
4279 const gop = try units.getOrPut(gpa, .wrap(.{ .nav_val = nav_id }));
4280 if (!gop.found_existing) {
4281 refs_log.debug("type '{f}': ref test %{}", .{
4282 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
4283 @backingInt(inst_info.inst),
4284 });
4285 gop.value_ptr.* = referencer;
4286 }
4287 }
4288 // Non-fatal AstGen errors could mean this test decl failed
4289 if (nav.resolved != null and nav.resolved.?.value != .none) {
4290 const gop = try units.getOrPut(gpa, .wrap(.{ .func = nav.resolved.?.value }));
4291 if (!gop.found_existing) gop.value_ptr.* = referencer;
4292 }
4293 }
4294 }
4295 for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
4296 // These are named declarations. They are analyzed only if marked `export`.
4297 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
4298 const file = zcu.fileByIndex(inst_info.file);
4299 const decl = file.zir.?.getDeclaration(inst_info.inst);
4300 if (decl.linkage == .@"export") {
4301 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
4302 const gop = try units.getOrPut(gpa, unit);
4303 if (!gop.found_existing) {
4304 refs_log.debug("type '{f}': ref named %{}", .{
4305 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
4306 @backingInt(inst_info.inst),
4307 });
4308 gop.value_ptr.* = referencer;
4309 }
4310 }
4311 }
4312 for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
4313 // These are named declarations. They are analyzed only if marked `export`.
4314 const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
4315 const file = zcu.fileByIndex(inst_info.file);
4316 const decl = file.zir.?.getDeclaration(inst_info.inst);
4317 if (decl.linkage == .@"export") {
4318 const unit: AnalUnit = .wrap(.{ .nav_val = nav });
4319 const gop = try units.getOrPut(gpa, unit);
4320 if (!gop.found_existing) {
4321 refs_log.debug("type '{f}': ref named %{}", .{
4322 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
4323 @backingInt(inst_info.inst),
4324 });
4325 gop.value_ptr.* = referencer;
4326 }
4327 }
4328 }
4329 continue;
4330 }
4331 if (unit_idx < units.count()) {
4332 const unit = units.keys()[unit_idx];
4333 unit_idx += 1;
4334
4335 // `nav_val` and `nav_ty` reference each other *implicitly* to save memory.
4336 // Likewise for `type_layout` and `struct_defaults` of a struct type.
4337 queue_paired: {
4338 const other: AnalUnit = .wrap(switch (unit.unwrap()) {
4339 .nav_val => |n| .{ .nav_ty = n },
4340 .nav_ty => |n| .{ .nav_val = n },
4341 .struct_defaults => |ty| .{ .type_layout = ty },
4342 .type_layout => |ty| switch (ip.indexToKey(ty)) {
4343 .struct_type => .{ .struct_defaults = ty },
4344 .union_type, .enum_type, .opaque_type => break :queue_paired,
4345 else => unreachable,
4346 },
4347 .@"comptime", .func, .memoized_state => break :queue_paired,
4348 });
4349 const gop = try units.getOrPut(gpa, other);
4350 if (gop.found_existing) break :queue_paired;
4351 gop.value_ptr.* = units.values()[unit_idx - 1]; // same reference location
4352 }
4353
4354 refs_log.debug("handle unit '{f}'", .{zcu.fmtAnalUnit(unit)});
4355
4356 if (zcu.reference_table.get(unit)) |first_ref_idx| {
4357 assert(first_ref_idx != std.math.maxInt(u32));
4358 var ref_idx = first_ref_idx;
4359 while (ref_idx != std.math.maxInt(u32)) {
4360 const ref = zcu.all_references.items[ref_idx];
4361 const gop = try units.getOrPut(gpa, ref.referenced);
4362 if (!gop.found_existing) {
4363 refs_log.debug("unit '{f}': ref unit '{f}'", .{
4364 zcu.fmtAnalUnit(unit),
4365 zcu.fmtAnalUnit(ref.referenced),
4366 });
4367 gop.value_ptr.* = .{
4368 .referencer = unit,
4369 .src = ref.src,
4370 .inline_frame = ref.inline_frame,
4371 };
4372 }
4373 ref_idx = ref.next;
4374 }
4375 }
4376 if (zcu.type_reference_table.get(unit)) |first_ref_idx| {
4377 assert(first_ref_idx != std.math.maxInt(u32));
4378 var ref_idx = first_ref_idx;
4379 while (ref_idx != std.math.maxInt(u32)) {
4380 const ref = zcu.all_type_references.items[ref_idx];
4381 const gop = try types.getOrPut(gpa, ref.referenced);
4382 if (!gop.found_existing) {
4383 refs_log.debug("unit '{f}': ref type '{f}'", .{
4384 zcu.fmtAnalUnit(unit),
4385 Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip),
4386 });
4387 gop.value_ptr.* = .{
4388 .referencer = unit,
4389 .src = ref.src,
4390 .inline_frame = .none,
4391 };
4392 }
4393 ref_idx = ref.next;
4394 }
4395 }
4396 continue;
4397 }
4398 break;
4399 }
4400
4401 return units.move();
4402}
4403
4404pub fn analysisRoots(zcu: *Zcu) []*Module {
4405 return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len];
4406}
4407
4408pub fn fileByIndex(zcu: *const Zcu, file_index: File.Index) *File {
4409 return zcu.intern_pool.filePtr(file_index);
4410}
4411
4412/// Returns the struct that represents this `File`.
4413/// If the struct has not been created, returns `.none`.
4414pub fn fileRootType(zcu: *const Zcu, file_index: File.Index) InternPool.Index {
4415 const ip = &zcu.intern_pool;
4416 const file_index_unwrapped = file_index.unwrap(ip);
4417 const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire();
4418 return files.view().items(.root_type)[file_index_unwrapped.index];
4419}
4420
4421pub fn setFileRootType(zcu: *Zcu, file_index: File.Index, root_type: InternPool.Index) void {
4422 const ip = &zcu.intern_pool;
4423 const file_index_unwrapped = file_index.unwrap(ip);
4424 const files = ip.getLocalShared(file_index_unwrapped.tid).files.acquire();
4425 files.view().items(.root_type)[file_index_unwrapped.index] = root_type;
4426}
4427
4428pub fn navSrcLoc(zcu: *const Zcu, nav_index: InternPool.Nav.Index) LazySrcLoc {
4429 const ip = &zcu.intern_pool;
4430 return .{
4431 .base_node_inst = ip.getNav(nav_index).srcInst(ip),
4432 .offset = LazySrcLoc.Offset.nodeOffset(.zero),
4433 };
4434}
4435
4436pub fn typeSrcLoc(zcu: *const Zcu, ty_index: InternPool.Index) LazySrcLoc {
4437 _ = zcu;
4438 _ = ty_index;
4439 @panic("TODO");
4440}
4441
4442pub fn typeFileScope(zcu: *Zcu, ty_index: InternPool.Index) *File {
4443 _ = zcu;
4444 _ = ty_index;
4445 @panic("TODO");
4446}
4447
4448pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
4449 const ip = &zcu.intern_pool;
4450 const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
4451 const zir = zcu.fileByIndex(inst_info.file).zir;
4452 return zir.?.getDeclaration(inst_info.inst).src_line;
4453}
4454
4455pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
4456 return .fromInterned(zcu.intern_pool.getNav(nav_index).resolved.?.value);
4457}
4458
4459pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index {
4460 const ip = &zcu.intern_pool;
4461 return ip.getNav(nav).srcInst(ip).resolveFile(ip);
4462}
4463
4464pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
4465 return zcu.fileByIndex(zcu.navFileScopeIndex(nav));
4466}
4467
4468pub fn navAlignment(zcu: *Zcu, nav_id: InternPool.Nav.Index) InternPool.Alignment {
4469 const resolved = zcu.intern_pool.getNav(nav_id).resolved.?;
4470 return switch (resolved.@"align") {
4471 else => |a| a,
4472 .none => Type.fromInterned(resolved.type).abiAlignment(zcu),
4473 };
4474}
4475
4476pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Alt(FormatAnalUnit, formatAnalUnit) {
4477 return .{ .data = .{ .unit = unit, .zcu = zcu } };
4478}
4479pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Alt(FormatDependee, formatDependee) {
4480 return .{ .data = .{ .dependee = d, .zcu = zcu } };
4481}
4482
4483const FormatAnalUnit = struct { unit: AnalUnit, zcu: *const Zcu };
4484fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void {
4485 const zcu = data.zcu;
4486 const ip = &zcu.intern_pool;
4487 switch (data.unit.unwrap()) {
4488 .@"comptime" => |cu_id| {
4489 const cu = ip.getComptimeUnit(cu_id);
4490 if (cu.zir_index.resolveFull(ip)) |resolved| {
4491 const file_path = zcu.fileByIndex(resolved.file).path;
4492 return writer.print("comptime(inst=('{f}', %{}) [{}])", .{ file_path.fmt(zcu.comp), @backingInt(resolved.inst), @backingInt(cu_id) });
4493 } else {
4494 return writer.print("comptime(inst=<lost> [{}])", .{@backingInt(cu_id)});
4495 }
4496 },
4497 .nav_val, .nav_ty => |nav, tag| return writer.print("{t}('{f}' [{}])", .{ tag, ip.getNav(nav).fqn.fmt(ip), @backingInt(nav) }),
4498 .type_layout, .struct_defaults => |ty, tag| return writer.print("{t}('{f}' [{}])", .{ tag, Type.fromInterned(ty).containerTypeName(ip).fmt(ip), @backingInt(ty) }),
4499 .func => |func| {
4500 const nav = zcu.funcInfo(func).owner_nav;
4501 return writer.print("func('{f}' [{}])", .{ ip.getNav(nav).fqn.fmt(ip), @backingInt(func) });
4502 },
4503 .memoized_state => return writer.writeAll("memoized_state"),
4504 }
4505}
4506
4507const FormatDependee = struct { dependee: InternPool.Dependee, zcu: *const Zcu };
4508fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void {
4509 const zcu = data.zcu;
4510 const ip = &zcu.intern_pool;
4511 switch (data.dependee) {
4512 .src_hash => |ti| {
4513 const info = ti.resolveFull(ip) orelse {
4514 return writer.writeAll("inst(<lost>)");
4515 };
4516 const file_path = zcu.fileByIndex(info.file).path;
4517 return writer.print("inst('{f}', %{d})", .{ file_path.fmt(zcu.comp), @backingInt(info.inst) });
4518 },
4519 .nav_val, .nav_ty => |nav, tag| {
4520 const fqn = ip.getNav(nav).fqn;
4521 return writer.print("{t}('{f}')", .{ tag, fqn.fmt(ip) });
4522 },
4523 .type_layout, .struct_defaults => |ip_index, tag| {
4524 const name = Type.fromInterned(ip_index).containerTypeName(ip);
4525 return writer.print("{t}('{f}')", .{ tag, name.fmt(ip) });
4526 },
4527 .func_ies => |ip_index| {
4528 const fqn = ip.getNav(ip.indexToKey(ip_index).func.owner_nav).fqn;
4529 return writer.print("func_ies('{f}')", .{fqn.fmt(ip)});
4530 },
4531 .source_file => |file| {
4532 const file_path = zcu.fileByIndex(file).path;
4533 return writer.print("source_file('{f}')", .{file_path.fmt(zcu.comp)});
4534 },
4535 .embed_file => |ef_idx| {
4536 const ef = ef_idx.get(zcu);
4537 return writer.print("embed_file('{f}')", .{ef.path.fmt(zcu.comp)});
4538 },
4539 .namespace => |ti| {
4540 const info = ti.resolveFull(ip) orelse {
4541 return writer.writeAll("namespace(<lost>)");
4542 };
4543 const file_path = zcu.fileByIndex(info.file).path;
4544 return writer.print("namespace('{f}', %{d})", .{ file_path.fmt(zcu.comp), @backingInt(info.inst) });
4545 },
4546 .namespace_name => |k| {
4547 const info = k.namespace.resolveFull(ip) orelse {
4548 return writer.print("namespace(<lost>, '{f}')", .{k.name.fmt(ip)});
4549 };
4550 const file_path = zcu.fileByIndex(info.file).path;
4551 return writer.print("namespace('{f}', %{d}, '{f}')", .{ file_path.fmt(zcu.comp), @backingInt(info.inst), k.name.fmt(ip) });
4552 },
4553 .memoized_state => return writer.writeAll("memoized_state"),
4554 }
4555}
4556
4557pub fn callconvSupported(zcu: *Zcu, cc: std.lang.CallingConvention) union(enum) {
4558 ok,
4559 bad_arch: []const std.Target.Cpu.Arch, // value is allowed archs for cc
4560 bad_backend: std.lang.CompilerBackend, // value is current backend
4561} {
4562 const target = zcu.getTarget();
4563 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
4564 switch (cc) {
4565 .auto, .@"inline" => return .ok,
4566 .async => return .{ .bad_backend = backend }, // nothing supports async currently
4567 .naked => {}, // depends only on backend
4568 else => for (cc.archs()) |allowed_arch| {
4569 if (allowed_arch == target.cpu.arch) break;
4570 } else return .{ .bad_arch = cc.archs() },
4571 }
4572 const backend_ok = switch (backend) {
4573 .stage1 => unreachable,
4574 .other => unreachable,
4575 _ => unreachable,
4576
4577 .stage2_llvm => @import("codegen/llvm.zig").toLlvmCallConv(cc, target) != null,
4578 .stage2_c => ok: {
4579 if (target.cCallingConvention()) |default_c| {
4580 if (cc.eql(default_c)) {
4581 break :ok true;
4582 }
4583 }
4584 break :ok switch (cc) {
4585 .x86_16_cdecl,
4586 .x86_16_stdcall,
4587 .x86_16_regparmcall,
4588 .x86_16_interrupt,
4589 .x86_64_sysv,
4590 .x86_64_win,
4591 .x86_64_vectorcall,
4592 .x86_64_regcall_v3_sysv,
4593 .x86_64_regcall_v4_win,
4594 .x86_64_interrupt,
4595 .x86_64_preserve_none,
4596 .x86_fastcall,
4597 .x86_thiscall,
4598 .x86_vectorcall,
4599 .x86_regcall_v3,
4600 .x86_regcall_v4_win,
4601 .x86_interrupt,
4602 .aarch64_vfabi,
4603 .aarch64_vfabi_sve,
4604 .aarch64_preserve_none,
4605 .arm_aapcs,
4606 .csky_interrupt,
4607 .riscv64_lp64_v,
4608 .riscv32_ilp32_v,
4609 .m68k_rtd,
4610 .m68k_interrupt,
4611 .msp430_interrupt,
4612 .arm_aapcs_vfp,
4613 .arc_interrupt,
4614 .arm_interrupt,
4615 .microblaze_interrupt,
4616 .mips_interrupt,
4617 .mips64_interrupt,
4618 .riscv32_interrupt,
4619 .riscv64_interrupt,
4620 .sh_interrupt,
4621 .avr_interrupt,
4622 .avr_signal,
4623 .ez80_tiflags,
4624 .naked,
4625 => true, // incoming stack alignment supported
4626
4627 .x86_sysv,
4628 .x86_win,
4629 .x86_mingw,
4630 .x86_stdcall,
4631 => |opts| opts.register_params == 0, // incoming stack alignment supported
4632
4633 else => false,
4634 };
4635 },
4636 .stage2_wasm => switch (cc) {
4637 .wasm_mvp => |opts| opts.incoming_stack_alignment == null,
4638 else => false,
4639 },
4640 .stage2_arm => switch (cc) {
4641 .arm_aapcs => |opts| opts.incoming_stack_alignment == null,
4642 .naked => true,
4643 else => false,
4644 },
4645 .stage2_x86_64 => switch (cc) {
4646 .x86_64_sysv, .x86_64_win, .naked => true, // incoming stack alignment supported
4647 else => false,
4648 },
4649 .stage2_aarch64 => switch (cc) {
4650 .aarch64_aapcs, .aarch64_aapcs_darwin, .naked => true,
4651 else => false,
4652 },
4653 .stage2_x86 => switch (cc) {
4654 .x86_sysv,
4655 .x86_win,
4656 .x86_mingw,
4657 => |opts| opts.incoming_stack_alignment == null and opts.register_params == 0,
4658 .naked => true,
4659 else => false,
4660 },
4661 .stage2_powerpc => switch (target.cpu.arch) {
4662 .powerpc, .powerpcle => switch (cc) {
4663 .powerpc_sysv,
4664 .powerpc_sysv_altivec,
4665 .powerpc_aix,
4666 .powerpc_aix_altivec,
4667 .naked,
4668 => true,
4669 else => false,
4670 },
4671 .powerpc64, .powerpc64le => switch (cc) {
4672 .powerpc64_elf,
4673 .powerpc64_elf_altivec,
4674 .powerpc64_elf_v2,
4675 .naked,
4676 => true,
4677 else => false,
4678 },
4679 else => unreachable,
4680 },
4681 .stage2_riscv64 => switch (cc) {
4682 .riscv64_lp64 => |opts| opts.incoming_stack_alignment == null,
4683 .naked => true,
4684 else => false,
4685 },
4686 .stage2_sparc64 => switch (cc) {
4687 .sparc64_sysv => |opts| opts.incoming_stack_alignment == null,
4688 .naked => true,
4689 else => false,
4690 },
4691 .stage2_spirv => switch (cc) {
4692 .spirv_device, .spirv_kernel => true,
4693 .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl,
4694 .spirv_task, .spirv_mesh => target.os.tag == .vulkan,
4695 else => false,
4696 },
4697 .stage2_loongarch => switch (cc) {
4698 .loongarch64_lp64, .loongarch32_ilp32, .naked => true,
4699 else => false,
4700 },
4701 .zsf_spork8 => switch (cc) {
4702 .spork8, .naked => true,
4703 else => false,
4704 },
4705 };
4706 if (!backend_ok) return .{ .bad_backend = backend };
4707 return .ok;
4708}
4709
4710pub const CodegenFailError = error{
4711 /// Indicates the error message has been already stored at `Zcu.failed_codegen`.
4712 AlreadyReported,
4713 OutOfMemory,
4714};
4715
4716pub fn codegenFail(
4717 zcu: *Zcu,
4718 nav_index: InternPool.Nav.Index,
4719 comptime format: []const u8,
4720 args: anytype,
4721) CodegenFailError {
4722 const msg = try Zcu.ErrorMsg.create(zcu.gpa, zcu.navSrcLoc(nav_index), format, args);
4723 return zcu.codegenFailMsg(nav_index, msg);
4724}
4725
4726/// Takes ownership of `msg`, even on OOM.
4727pub fn codegenFailMsg(zcu: *Zcu, nav_index: InternPool.Nav.Index, msg: *ErrorMsg) CodegenFailError {
4728 const comp = zcu.comp;
4729 const gpa = comp.gpa;
4730 const io = comp.io;
4731 {
4732 comp.mutex.lockUncancelable(io);
4733 defer comp.mutex.unlock(io);
4734 errdefer msg.deinit(gpa);
4735 try zcu.failed_codegen.putNoClobber(gpa, nav_index, msg);
4736 }
4737 return error.AlreadyReported;
4738}
4739
4740pub fn codegenFailType(
4741 zcu: *Zcu,
4742 ty_index: InternPool.Index,
4743 comptime format: []const u8,
4744 args: anytype,
4745) CodegenFailError {
4746 const gpa = zcu.gpa;
4747 try zcu.failed_types.ensureUnusedCapacity(gpa, 1);
4748 const msg = try Zcu.ErrorMsg.create(gpa, zcu.typeSrcLoc(ty_index), format, args);
4749 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
4750 return error.AlreadyReported;
4751}
4752
4753pub fn codegenFailTypeMsg(zcu: *Zcu, ty_index: InternPool.Index, msg: *ErrorMsg) CodegenFailError {
4754 const gpa = zcu.gpa;
4755 {
4756 errdefer msg.deinit(gpa);
4757 try zcu.failed_types.ensureUnusedCapacity(gpa, 1);
4758 }
4759 zcu.failed_types.putAssumeCapacityNoClobber(ty_index, msg);
4760 return error.AlreadyReported;
4761}
4762
4763/// Asserts that `zcu.multi_module_err != null`.
4764pub fn addFileInMultipleModulesError(
4765 zcu: *Zcu,
4766 eb: *std.zig.ErrorBundle.Wip,
4767) Allocator.Error!void {
4768 const gpa = zcu.gpa;
4769
4770 const info = zcu.multi_module_err.?;
4771 const file = info.file;
4772
4773 // error: file exists in modules 'root.foo' and 'root.bar'
4774 // note: files must belong to only one module
4775 // note: file is imported here
4776 // note: which is imported here
4777 // note: which is the root of module 'root.foo' imported here
4778 // note: file is the root of module 'root.bar' imported here
4779
4780 const file_src = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb);
4781 const root_msg = try eb.printString("file exists in modules '{s}' and '{s}'", .{
4782 info.modules[0].fully_qualified_name,
4783 info.modules[1].fully_qualified_name,
4784 });
4785
4786 var notes: std.ArrayList(std.zig.ErrorBundle.MessageIndex) = .empty;
4787 defer notes.deinit(gpa);
4788
4789 try notes.append(gpa, try eb.addErrorMessage(.{
4790 .msg = try eb.addString("files must belong to only one module"),
4791 .src_loc = file_src,
4792 }));
4793
4794 try zcu.explainWhyFileIsInModule(eb, &notes, file, info.modules[0], info.refs[0]);
4795 try zcu.explainWhyFileIsInModule(eb, &notes, file, info.modules[1], info.refs[1]);
4796
4797 try eb.addRootErrorMessage(.{
4798 .msg = root_msg,
4799 .src_loc = file_src,
4800 .notes_len = @intCast(notes.items.len),
4801 });
4802 const notes_start = try eb.reserveNotes(@intCast(notes.items.len));
4803 const notes_slice: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]);
4804 @memcpy(notes_slice, notes.items);
4805}
4806
4807fn explainWhyFileIsInModule(
4808 zcu: *Zcu,
4809 eb: *std.zig.ErrorBundle.Wip,
4810 notes_out: *std.ArrayList(std.zig.ErrorBundle.MessageIndex),
4811 file: File.Index,
4812 in_module: *Module,
4813 ref: File.Reference,
4814) Allocator.Error!void {
4815 const gpa = zcu.gpa;
4816
4817 // error: file is the root of module 'foo'
4818 //
4819 // error: file is imported here by the root of module 'foo'
4820 //
4821 // error: file is imported here
4822 // note: which is imported here
4823 // note: which is imported here by the root of module 'foo'
4824
4825 var import = switch (ref) {
4826 .analysis_root => |mod| {
4827 assert(mod == in_module);
4828 try notes_out.append(gpa, try eb.addErrorMessage(.{
4829 .msg = try eb.printString("file is the root of module '{s}'", .{mod.fully_qualified_name}),
4830 .src_loc = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb),
4831 }));
4832 return;
4833 },
4834 .import => |import| if (import.module) |mod| {
4835 assert(mod == in_module);
4836 try notes_out.append(gpa, try eb.addErrorMessage(.{
4837 .msg = try eb.printString("file is the root of module '{s}'", .{mod.fully_qualified_name}),
4838 .src_loc = try zcu.fileByIndex(file).errorBundleWholeFileSrc(zcu, eb),
4839 }));
4840 return;
4841 } else import,
4842 };
4843
4844 var is_first = true;
4845 while (true) {
4846 const thing: []const u8 = if (is_first) "file" else "which";
4847 is_first = false;
4848
4849 const importer_file = zcu.fileByIndex(import.importer);
4850 // `errorBundleTokenSrc` expects the tree to be loaded
4851 _ = importer_file.getTree(zcu) catch |err| {
4852 try Compilation.unableToLoadZcuFile(zcu, eb, importer_file, err);
4853 return; // stop the explanation early
4854 };
4855 const import_src = try importer_file.errorBundleTokenSrc(import.tok, zcu, eb);
4856
4857 const importer_ref = zcu.alive_files.get(import.importer).?;
4858 const importer_root: ?*Module = switch (importer_ref) {
4859 .analysis_root => |mod| mod,
4860 .import => |i| i.module,
4861 };
4862
4863 if (importer_root) |m| {
4864 try notes_out.append(gpa, try eb.addErrorMessage(.{
4865 .msg = try eb.printString("{s} is imported here by the root of module '{s}'", .{ thing, m.fully_qualified_name }),
4866 .src_loc = import_src,
4867 }));
4868 return;
4869 }
4870
4871 try notes_out.append(gpa, try eb.addErrorMessage(.{
4872 .msg = try eb.printString("{s} is imported here", .{thing}),
4873 .src_loc = import_src,
4874 }));
4875
4876 import = importer_ref.import;
4877 }
4878}
4879
4880pub fn addDependencyLoopErrors(zcu: *Zcu, eb: *std.zig.ErrorBundle.Wip) Allocator.Error!void {
4881 const gpa = zcu.comp.gpa;
4882
4883 const all_references = try zcu.resolveReferences();
4884
4885 var units: std.ArrayList(AnalUnit) = .empty;
4886 defer units.deinit(gpa);
4887
4888 // TODO: sort the dependency loops somehow to make the error bundle reproducible
4889 for (zcu.dependency_loops.keys()) |arbitrary_unit| {
4890 units.clearRetainingCapacity();
4891
4892 var cur = arbitrary_unit;
4893 while (true) {
4894 try units.append(gpa, cur);
4895 cur = zcu.dependency_loop_nodes.get(cur).?.unit;
4896 if (cur == arbitrary_unit) break;
4897 }
4898
4899 // `units` now contains all units in the loop. We need to pick a starting point somewhere
4900 // along that loop to begin. We will pick whichever node has the shortest reference trace,
4901 // because the other units may well just be referenced *by* that one! This is also likely
4902 // to match the user's intuition for where the loop "starts".
4903 var start_index: usize = 0;
4904 var start_depth: u32 = depth: {
4905 var depth: u32 = 0;
4906 var opt_ref = all_references.get(units.items[0]) orelse {
4907 // This dependency loop is actually unreferenced, so we don't need to emit a compile
4908 // error at all! Move onto the next dependency loop.
4909 continue;
4910 };
4911 while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1;
4912 break :depth depth;
4913 };
4914 for (units.items[1..], 1..) |unit, index| {
4915 var depth: u32 = 0;
4916 var opt_ref = all_references.get(unit).?;
4917 while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1;
4918 if (depth < start_depth) {
4919 start_index = index;
4920 start_depth = depth;
4921 }
4922 }
4923
4924 // Collect a reference trace for the start of the loop.
4925 var ref_trace: std.ArrayList(std.zig.ErrorBundle.ReferenceTrace) = .empty;
4926 defer ref_trace.deinit(gpa);
4927 const frame_limit = zcu.comp.reference_trace orelse 0;
4928 try zcu.populateReferenceTrace(units.items[start_index], frame_limit, eb, &ref_trace);
4929
4930 if (units.items.len == 1) {
4931 // Don't do a complicated message with multiple notes, just do a single error message.
4932 assert(start_index == 0);
4933 const root_msg = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) {
4934 error.AlreadyReported => return, // give up on the dep loop error
4935 error.OutOfMemory => |e| return e,
4936 };
4937 try eb.root_list.append(eb.gpa, root_msg);
4938 continue;
4939 }
4940
4941 // Collect all notes first so we don't leave an incomplete root error message on `error.AlreadyReported`.
4942 const note_buf = try gpa.alloc(std.zig.ErrorBundle.MessageIndex, units.items.len + 1);
4943 defer gpa.free(note_buf);
4944 note_buf[0] = addDependencyLoopErrorLine(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) {
4945 error.AlreadyReported => return, // give up on the dep loop error
4946 error.OutOfMemory => |e| return e,
4947 };
4948 for (units.items[start_index + 1 ..], note_buf[1 .. units.items.len - start_index]) |unit, *note| {
4949 note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) {
4950 error.AlreadyReported => return, // give up on the dep loop error
4951 error.OutOfMemory => |e| return e,
4952 };
4953 }
4954 for (units.items[0..start_index], note_buf[units.items.len - start_index .. units.items.len]) |unit, *note| {
4955 note.* = addDependencyLoopErrorLine(zcu, eb, unit, &.{}) catch |err| switch (err) {
4956 error.AlreadyReported => return, // give up on the dep loop error
4957 error.OutOfMemory => |e| return e,
4958 };
4959 }
4960 note_buf[units.items.len] = try eb.addErrorMessage(.{
4961 .msg = try eb.addString("eliminate any one of these dependencies to break the loop"),
4962 .src_loc = .none,
4963 });
4964
4965 try eb.addRootErrorMessage(.{
4966 .msg = try eb.printString("dependency loop with length {d}", .{units.items.len}),
4967 .src_loc = .none,
4968 .notes_len = @intCast(units.items.len + 1),
4969 });
4970 const notes_start = try eb.reserveNotes(@intCast(units.items.len + 1));
4971 const notes: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]);
4972 @memcpy(notes, note_buf);
4973 }
4974}
4975fn addDependencyLoopErrorLine(
4976 zcu: *Zcu,
4977 eb: *std.zig.ErrorBundle.Wip,
4978 source_unit: AnalUnit,
4979 ref_trace: []const std.zig.ErrorBundle.ReferenceTrace,
4980) (Allocator.Error || error{AlreadyReported})!std.zig.ErrorBundle.MessageIndex {
4981 const ip = &zcu.intern_pool;
4982 const comp = zcu.comp;
4983
4984 const fmt_source: std.fmt.Alt(FormatAnalUnit, formatDependencyLoopSourceUnit) = .{ .data = .{
4985 .unit = source_unit,
4986 .zcu = zcu,
4987 } };
4988
4989 const dep_node = zcu.dependency_loop_nodes.get(source_unit).?;
4990
4991 const msg: std.zig.ErrorBundle.String = if (dep_node.unit == source_unit) switch (source_unit.unwrap()) {
4992 .@"comptime" => unreachable, // cannot be involved in a dependency loop
4993 .nav_ty, .nav_val => try eb.printString("{f} depends on itself here", .{fmt_source}),
4994 .memoized_state => unreachable, // memoized_state definitely does not *directly* depend on itself
4995 .func => try eb.printString("{f} uses its own inferred error set here", .{fmt_source}),
4996 .type_layout => try eb.printString("{f} depends on itself {s}", .{
4997 fmt_source,
4998 dep_node.reason.type_layout_reason.msg(),
4999 }),
5000 .struct_defaults => |ty| try eb.printString(
5001 "default field values of '{f}' depend on themselves for initialization here",
5002 .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)},
5003 ),
5004 } else switch (dep_node.unit.unwrap()) {
5005 .@"comptime" => unreachable, // cannot be involved in a dependency loop
5006 .nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{
5007 fmt_source, ip.getNav(nav).fqn.fmt(ip),
5008 }),
5009 .nav_ty => |nav| try eb.printString("{f} uses type of declaration '{f}' here", .{
5010 fmt_source, ip.getNav(nav).fqn.fmt(ip),
5011 }),
5012 .memoized_state => |stage| switch (stage) {
5013 .panic => try eb.printString("{f} requires panic handler for call here", .{fmt_source}),
5014 else => try eb.printString("{f} requires 'std.lang' declarations here", .{fmt_source}),
5015 },
5016 .func => |func| try eb.printString("{f} uses inferred error set of function '{f}' here", .{
5017 fmt_source, ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip),
5018 }),
5019 .type_layout => |ty| try eb.printString("{f} depends on type '{f}' {s}", .{
5020 fmt_source,
5021 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
5022 dep_node.reason.type_layout_reason.msg(),
5023 }),
5024 .struct_defaults => |ty| try eb.printString(
5025 "{f} uses default field values of '{f}' here",
5026 .{ fmt_source, Type.fromInterned(ty).containerTypeName(ip).fmt(ip) },
5027 ),
5028 };
5029
5030 const src_loc = dep_node.reason.src.upgrade(zcu);
5031 const source = src_loc.file_scope.getSource(zcu) catch |err| {
5032 try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err);
5033 return error.AlreadyReported;
5034 };
5035 const span = src_loc.span(zcu) catch |err| {
5036 try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err);
5037 return error.AlreadyReported;
5038 };
5039 const loc = std.zig.findLineColumn(source, span.main);
5040 const eb_src = try eb.addSourceLocation(.{
5041 .src_path = try eb.printString("{f}", .{src_loc.file_scope.path.fmt(comp)}),
5042 .span_start = span.start,
5043 .span_main = span.main,
5044 .span_end = span.end,
5045 .line = @intCast(loc.line),
5046 .column = @intCast(loc.column),
5047 .source_line = try eb.addString(loc.source_line),
5048 .reference_trace_len = @intCast(ref_trace.len),
5049 });
5050 for (ref_trace) |rt| try eb.addReferenceTrace(rt);
5051 return eb.addErrorMessage(.{
5052 .msg = msg,
5053 .src_loc = eb_src,
5054 });
5055}
5056fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer.Error!void {
5057 const zcu = data.zcu;
5058 const ip = &zcu.intern_pool;
5059 switch (data.unit.unwrap()) {
5060 .@"comptime" => unreachable, // cannot be involved in a dependency loop
5061 .nav_val => |nav| try w.print("value of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}),
5062 .nav_ty => |nav| try w.print("type of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}),
5063 .memoized_state => |stage| switch (stage) {
5064 .panic => try w.writeAll("panic handler"),
5065 else => try w.writeAll("'std.lang' declarations"),
5066 },
5067 .type_layout => |ty| try w.print("type '{f}'", .{
5068 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
5069 }),
5070 .struct_defaults => |ty| try w.print("default field value of '{f}'", .{
5071 Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
5072 }),
5073 .func => |func| try w.print("function '{f}'", .{
5074 ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip),
5075 }),
5076 }
5077}
5078
5079pub fn populateReferenceTrace(
5080 zcu: *Zcu,
5081 root: AnalUnit,
5082 frame_limit: u32,
5083 eb: *std.zig.ErrorBundle.Wip,
5084 ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace),
5085) Allocator.Error!void {
5086 const ip = &zcu.intern_pool;
5087 const gpa = zcu.comp.gpa;
5088
5089 if (frame_limit == 0) return;
5090
5091 const all_references = try zcu.resolveReferences();
5092
5093 var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty;
5094 defer seen.deinit(gpa);
5095
5096 var referenced_by = root;
5097 while (all_references.get(referenced_by)) |maybe_ref| {
5098 const ref = maybe_ref orelse break;
5099 const gop = try seen.getOrPut(gpa, ref.referencer);
5100 if (gop.found_existing) break;
5101 if (ref_trace.items.len < frame_limit) {
5102 var last_call_src = ref.src;
5103 var opt_inline_frame = ref.inline_frame;
5104 while (opt_inline_frame.unwrap()) |inline_frame| {
5105 const f = inline_frame.ptr(zcu).*;
5106 const func_nav = ip.indexToKey(f.callee).func.owner_nav;
5107 const func_name = ip.getNav(func_nav).name.toSlice(ip);
5108 addReferenceTraceFrame(zcu, eb, ref_trace, func_name, last_call_src, true) catch |err| switch (err) {
5109 error.OutOfMemory => |e| return e,
5110 error.AlreadyReported => {
5111 // An incomplete reference trace isn't the end of the world; just cut it off.
5112 return;
5113 },
5114 };
5115 last_call_src = f.call_src;
5116 opt_inline_frame = f.parent;
5117 }
5118 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
5119 .@"comptime" => "comptime",
5120 .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
5121 .type_layout, .struct_defaults => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
5122 .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
5123 .memoized_state => null,
5124 };
5125 if (root_name) |n| {
5126 addReferenceTraceFrame(zcu, eb, ref_trace, n, last_call_src, false) catch |err| switch (err) {
5127 error.OutOfMemory => |e| return e,
5128 error.AlreadyReported => {
5129 // An incomplete reference trace isn't the end of the world; just cut it off.
5130 return;
5131 },
5132 };
5133 }
5134 }
5135 referenced_by = ref.referencer;
5136 }
5137
5138 if (seen.count() > ref_trace.items.len) {
5139 try ref_trace.append(gpa, .{
5140 .decl_name = @intCast(seen.count() - ref_trace.items.len),
5141 .src_loc = .none,
5142 });
5143 }
5144}
5145fn addReferenceTraceFrame(
5146 zcu: *Zcu,
5147 eb: *std.zig.ErrorBundle.Wip,
5148 ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace),
5149 name: []const u8,
5150 lazy_src: Zcu.LazySrcLoc,
5151 inlined: bool,
5152) error{ OutOfMemory, AlreadyReported }!void {
5153 const gpa = zcu.gpa;
5154 const src = lazy_src.upgrade(zcu);
5155 const source = src.file_scope.getSource(zcu) catch |err| {
5156 try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err);
5157 return error.AlreadyReported;
5158 };
5159 const span = src.span(zcu) catch |err| {
5160 try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err);
5161 return error.AlreadyReported;
5162 };
5163 const loc = std.zig.findLineColumn(source, span.main);
5164 try ref_trace.append(gpa, .{
5165 .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
5166 .src_loc = try eb.addSourceLocation(.{
5167 .src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}),
5168 .span_start = span.start,
5169 .span_main = span.main,
5170 .span_end = span.end,
5171 .line = @intCast(loc.line),
5172 .column = @intCast(loc.column),
5173 .source_line = 0,
5174 }),
5175 });
5176}
5177
5178const TrackedUnitSema = struct {
5179 /// `null` means we created the node, so should end it.
5180 old_name: ?[std.Progress.Node.max_name_len]u8,
5181 old_analysis_timer: ?Compilation.Timer,
5182 analysis_timer_decl: ?InternPool.TrackedInst.Index,
5183 pub fn end(tus: TrackedUnitSema, zcu: *Zcu) void {
5184 const comp = zcu.comp;
5185 const io = comp.io;
5186 if (tus.old_name) |old_name| {
5187 zcu.sema_prog_node.completeOne(); // we're just renaming, but it's effectively completion
5188 zcu.cur_sema_prog_node.setName(&old_name);
5189 } else {
5190 zcu.cur_sema_prog_node.end();
5191 zcu.cur_sema_prog_node = .none;
5192 }
5193 report_time: {
5194 const sema_ns = zcu.cur_analysis_timer.?.finish(io) orelse break :report_time;
5195 const zir_decl = tus.analysis_timer_decl orelse break :report_time;
5196 comp.mutex.lockUncancelable(io);
5197 defer comp.mutex.unlock(io);
5198 comp.time_report.?.stats.cpu_ns_sema += sema_ns;
5199 const gop = comp.time_report.?.decl_sema_info.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) {
5200 error.OutOfMemory => {
5201 comp.setAllocFailure();
5202 break :report_time;
5203 },
5204 };
5205 if (!gop.found_existing) gop.value_ptr.* = .{ .ns = 0, .count = 0 };
5206 gop.value_ptr.ns += sema_ns;
5207 gop.value_ptr.count += 1;
5208 }
5209 zcu.cur_analysis_timer = tus.old_analysis_timer;
5210 if (zcu.cur_analysis_timer) |*t| t.@"resume"(io);
5211 }
5212};
5213pub fn trackUnitSema(zcu: *Zcu, name: []const u8, zir_inst: ?InternPool.TrackedInst.Index) TrackedUnitSema {
5214 const comp = zcu.comp;
5215 const io = comp.io;
5216 if (zcu.cur_analysis_timer) |*t| t.pause(io);
5217 const old_analysis_timer = zcu.cur_analysis_timer;
5218 zcu.cur_analysis_timer = zcu.comp.startTimer();
5219 const old_name: ?[std.Progress.Node.max_name_len]u8 = old_name: {
5220 if (zcu.cur_sema_prog_node.index == .none) {
5221 zcu.cur_sema_prog_node = zcu.sema_prog_node.start(name, 0);
5222 break :old_name null;
5223 }
5224 const old_name = zcu.cur_sema_prog_node.getName();
5225 zcu.cur_sema_prog_node.setName(name);
5226 break :old_name old_name;
5227 };
5228 return .{
5229 .old_name = old_name,
5230 .old_analysis_timer = old_analysis_timer,
5231 .analysis_timer_decl = zir_inst,
5232 };
5233}
5234
5235pub const CodegenTaskPool = struct {
5236 const CodegenResult = PerThread.RunCodegenError!codegen.AnyMir;
5237
5238 /// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is
5239 /// around 20. Going by that 50x multiplier, and assuming we want to consume no more than 500 MiB of
5240 /// memory on AIR/MIR, we see a limit of around 10 MiB of AIR in-flight.
5241 const max_air_bytes_in_flight = 10 * 1024 * 1024;
5242
5243 const max_funcs_in_flight = @import("link.zig").Queue.buffer_size;
5244
5245 available_air_bytes: u32,
5246
5247 /// Locks the freelist and `available_air_bytes`.
5248 mutex: Io.Mutex,
5249
5250 /// Signaled when an item is added to the freelist.
5251 free_cond: Io.Condition,
5252 /// Pre-allocated with enough capacity for all indices.
5253 free: std.ArrayList(Index),
5254
5255 /// `.none` means this task is in the freelist. The `task_air_bytes` and
5256 /// `task_futures` entries are `undefined`.
5257 task_funcs: []InternPool.Index,
5258 task_air_bytes: []u32,
5259 task_futures: []Io.Future(CodegenResult),
5260
5261 pub fn init(arena: Allocator) Allocator.Error!CodegenTaskPool {
5262 const task_funcs = try arena.alloc(InternPool.Index, max_funcs_in_flight);
5263 const task_air_bytes = try arena.alloc(u32, max_funcs_in_flight);
5264 const task_futures = try arena.alloc(Io.Future(CodegenResult), max_funcs_in_flight);
5265 @memset(task_funcs, .none);
5266
5267 var free: std.ArrayList(Index) = try .initCapacity(arena, max_funcs_in_flight);
5268 for (0..max_funcs_in_flight) |index| free.appendAssumeCapacity(@fromBackingInt(@intCast(index)));
5269
5270 return .{
5271 .available_air_bytes = max_air_bytes_in_flight,
5272 .mutex = .init,
5273 .free_cond = .init,
5274 .free = free,
5275 .task_funcs = task_funcs,
5276 .task_air_bytes = task_air_bytes,
5277 .task_futures = task_futures,
5278 };
5279 }
5280
5281 pub fn cancel(pool: *CodegenTaskPool, zcu: *const Zcu) void {
5282 const io = zcu.comp.io;
5283 for (
5284 pool.task_funcs,
5285 pool.task_air_bytes,
5286 pool.task_futures,
5287 ) |func, effective_air_bytes, *future| {
5288 if (func == .none) continue;
5289 pool.available_air_bytes += effective_air_bytes;
5290 var mir = future.cancel(io) catch continue;
5291 mir.deinit(zcu);
5292 }
5293 assert(pool.available_air_bytes == max_air_bytes_in_flight);
5294 zcu.updateTracyPlot("air_bytes_in_flight", 0);
5295 }
5296
5297 pub fn start(
5298 pool: *CodegenTaskPool,
5299 zcu: *Zcu,
5300 func_index: InternPool.Index,
5301 air: *Air,
5302 /// If `true`, this function will take ownership of `air`, freeing it after codegen
5303 /// completes; it is not assumed that `air` will outlive this function. If `false`,
5304 /// codegen will operate on `air` via the given pointer, which it is assumed will
5305 /// outline the codegen task.
5306 move_air: bool,
5307 ) Io.Cancelable!Index {
5308 const io = zcu.comp.io;
5309
5310 // To avoid consuming an excessive amount of memory, there is a limit on the total number of AIR
5311 // bytes which can be in the codegen/link pipeline at one time. If we exceed this limit, we must
5312 // wait for codegen/link to finish some WIP functions so they catch up with us.
5313 const actual_air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4);
5314 // We need to let all AIR through eventually, even if one function exceeds `max_air_bytes_in_flight`.
5315 const effective_air_bytes: u32 = @min(actual_air_bytes, max_air_bytes_in_flight);
5316 assert(effective_air_bytes > 0);
5317
5318 const index: Index = index: {
5319 try pool.mutex.lock(io);
5320 defer pool.mutex.unlock(io);
5321
5322 while (pool.free.items.len == 0 or pool.available_air_bytes < effective_air_bytes) {
5323 // The linker thread needs to catch up!
5324 try pool.free_cond.wait(io, &pool.mutex);
5325 }
5326
5327 pool.available_air_bytes -= effective_air_bytes;
5328
5329 zcu.updateTracyPlot("air_bytes_in_flight", @max(
5330 max_air_bytes_in_flight - pool.available_air_bytes,
5331 actual_air_bytes,
5332 ));
5333
5334 break :index pool.free.pop().?;
5335 };
5336
5337 // No turning back now: we're incrementing `pending_codegen_jobs` and starting the worker.
5338 errdefer comptime unreachable;
5339
5340 assert(zcu.pending_codegen_jobs.fetchAdd(1, .monotonic) > 0); // the "Code Generation" node is still active
5341 assert(pool.task_funcs[@backingInt(index)] == .none);
5342 pool.task_funcs[@backingInt(index)] = func_index;
5343 pool.task_air_bytes[@backingInt(index)] = actual_air_bytes;
5344 pool.task_futures[@backingInt(index)] = if (move_air) io.async(
5345 workerCodegenOwnedAir,
5346 .{ zcu, func_index, air.* },
5347 ) else io.async(
5348 workerCodegenExternalAir,
5349 .{ zcu, func_index, air },
5350 );
5351
5352 return index;
5353 }
5354 pub const Index = enum(u32) {
5355 _,
5356
5357 /// Blocks until codegen has completed, successfully or otherwise.
5358 /// The returned MIR is owned by the caller.
5359 pub fn wait(
5360 index: Index,
5361 pool: *CodegenTaskPool,
5362 zcu: *const Zcu,
5363 ) PerThread.RunCodegenError!struct { InternPool.Index, codegen.AnyMir } {
5364 const io = zcu.comp.io;
5365 const func = pool.task_funcs[@backingInt(index)];
5366 assert(func != .none);
5367 const effective_air_bytes = pool.task_air_bytes[@backingInt(index)];
5368 const result = pool.task_futures[@backingInt(index)].await(io);
5369
5370 pool.task_funcs[@backingInt(index)] = .none;
5371 pool.task_air_bytes[@backingInt(index)] = undefined;
5372 pool.task_futures[@backingInt(index)] = undefined;
5373
5374 {
5375 pool.mutex.lockUncancelable(io);
5376 defer pool.mutex.unlock(io);
5377 pool.available_air_bytes += effective_air_bytes;
5378 pool.free.appendAssumeCapacity(index);
5379 pool.free_cond.signal(io);
5380 zcu.updateTracyPlot("air_bytes_in_flight", max_air_bytes_in_flight - pool.available_air_bytes);
5381 }
5382
5383 return .{ func, try result };
5384 }
5385 };
5386 fn workerCodegenOwnedAir(
5387 zcu: *Zcu,
5388 func_index: InternPool.Index,
5389 orig_air: Air,
5390 ) CodegenResult {
5391 // We own `air` now, so we are responsbile for freeing it.
5392 var air = orig_air;
5393 defer air.deinit(zcu.comp.gpa);
5394 const io = zcu.comp.io;
5395 const tid: Zcu.PerThread.Id = .acquire(io);
5396 defer tid.release(io);
5397 const active = zcu.activate(tid);
5398 defer active.deactivate();
5399 return active.pt.runCodegen(func_index, &air);
5400 }
5401 fn workerCodegenExternalAir(
5402 zcu: *Zcu,
5403 func_index: InternPool.Index,
5404 air: *Air,
5405 ) CodegenResult {
5406 const io = zcu.comp.io;
5407 const tid: Zcu.PerThread.Id = .acquire(io);
5408 defer tid.release(io);
5409 const active = zcu.activate(tid);
5410 defer active.deactivate();
5411 return active.pt.runCodegen(func_index, air);
5412 }
5413};
5414
5415fn initTracyPlots(zcu: *const Zcu) void {
5416 if (zcu.comp.skip_linker_dependencies) return;
5417
5418 tracy.plotConfig("air_bytes_in_flight", .{ .format = .memory, .mode = .step });
5419
5420 tracy.plotConfig("outdated + potentially_outdated", .{ .format = .number, .mode = .step, .color = 0xFFFF00 });
5421 tracy.plotConfig("outdated", .{ .format = .number, .mode = .step, .color = 0xFF0000 });
5422 tracy.plotConfig("potentially_outdated", .{ .format = .number, .mode = .step, .color = 0xFF7700 });
5423 tracy.plotConfig("outdated_ready", .{ .format = .number, .mode = .step, .color = 0x00FF00 });
5424}
5425
5426/// Marked `inline` to prevent binary bloat from trivial generic instances, and to ensure there is
5427/// minimal overhead to this call when Tracy is disabled, even in Debug builds.
5428inline fn updateTracyPlot(zcu: *const Zcu, comptime name: [*:0]const u8, val: u64) void {
5429 if (zcu.comp.skip_linker_dependencies) return;
5430 tracy.plotInt(name, @intCast(val));
5431}
5432
5433/// Assumes that `zcu.outdated_lock` is already held.
5434fn updateTracyOutdatedPlots(zcu: *const Zcu) void {
5435 zcu.updateTracyPlot("outdated + potentially_outdated", zcu.outdated.count() + zcu.potentially_outdated.count());
5436 zcu.updateTracyPlot("outdated", zcu.outdated.count());
5437 zcu.updateTracyPlot("potentially_outdated", zcu.potentially_outdated.count());
5438 zcu.updateTracyPlot("outdated_ready", zcu.outdated_ready.funcs.count() + zcu.outdated_ready.other.count());
5439}
5440
5441pub const Active = struct {
5442 pt: Zcu.PerThread,
5443 ip: InternPool.Active,
5444 pub fn deactivate(active: Active) void {
5445 active.ip.deactivate();
5446 }
5447 pub fn release(active: Active) void {
5448 active.deactivate();
5449 active.pt.tid.release(active.pt.zcu.comp.io);
5450 }
5451};
5452pub fn activate(zcu: *Zcu, tid: PerThread.Id) Active {
5453 return .{
5454 .pt = .{ .zcu = zcu, .tid = tid },
5455 .ip = zcu.intern_pool.activate(),
5456 };
5457}
5458pub fn acquire(zcu: *Zcu) Active {
5459 return zcu.activate(.acquire(zcu.comp.io));
5460}