authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-06-22 16:10:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-06-22 22:59:56-04:00
log0fcd59eadae468284943895f50bc9fc6d1924154
tree45e95f4bf8057a80d18dc78f6fea00ac57768455
parent6026bbd0adb445a0edd6defb0b378937dcce7a9b

rename src/Module.zig to src/Zcu.zig

This patch is a pure rename plus only changing the file path in `@import` sites, so it is expected to not create version control conflicts, even when rebasing.

58 files changed, 6570 insertions(+), 6501 deletions(-)

CMakeLists.txt+1-1
......@@ -513,7 +513,6 @@ set(ZIG_STAGE2_SOURCES
513513 src/InternPool.zig
514514 src/Liveness.zig
515515 src/Liveness/Verify.zig
516 src/Module.zig
517516 src/Package.zig
518517 src/Package/Fetch.zig
519518 src/Package/Fetch/git.zig
......@@ -524,6 +523,7 @@ set(ZIG_STAGE2_SOURCES
524523 src/Sema/bitcast.zig
525524 src/Sema/comptime_ptr_access.zig
526525 src/Value.zig
526 src/Zcu.zig
527527 src/arch/aarch64/CodeGen.zig
528528 src/arch/aarch64/Emit.zig
529529 src/arch/aarch64/Mir.zig
src/Air.zig+3-1
......@@ -11,7 +11,9 @@ const Air = @This();
1111const Value = @import("Value.zig");
1212const Type = @import("type.zig").Type;
1313const InternPool = @import("InternPool.zig");
14const Module = @import("Module.zig");
14const Zcu = @import("Zcu.zig");
15/// Deprecated.
16const Module = Zcu;
1517
1618instructions: std.MultiArrayList(Inst).Slice,
1719/// The meaning of this data is determined by `Inst.Tag` value.
src/Builtin.zig+1-1
......@@ -296,6 +296,6 @@ const build_options = @import("build_options");
296296const Module = @import("Package/Module.zig");
297297const assert = std.debug.assert;
298298const AstGen = std.zig.AstGen;
299const File = @import("Module.zig").File;
299const File = @import("Zcu.zig").File;
300300const Compilation = @import("Compilation.zig");
301301const log = std.log.scoped(.builtin);
src/Compilation.zig+1-1
......@@ -28,7 +28,7 @@ const libcxx = @import("libcxx.zig");
2828const wasi_libc = @import("wasi_libc.zig");
2929const fatal = @import("main.zig").fatal;
3030const clangMain = @import("main.zig").clangMain;
31const Zcu = @import("Module.zig");
31const Zcu = @import("Zcu.zig");
3232/// Deprecated; use `Zcu`.
3333const Module = Zcu;
3434const InternPool = @import("InternPool.zig");
src/InternPool.zig+3-2
......@@ -344,8 +344,9 @@ const Limb = std.math.big.Limb;
344344const Hash = std.hash.Wyhash;
345345
346346const InternPool = @This();
347const Module = @import("Module.zig");
348const Zcu = Module;
347const Zcu = @import("Zcu.zig");
348/// Deprecated.
349const Module = Zcu;
349350const Zir = std.zig.Zir;
350351
351352const KeyAdapter = struct {
src/Module.zig deleted-6427
......@@ -1,6427 +0,0 @@
1//! Compilation of all Zig source code is represented by one `Module`.
2//! Each `Compilation` has exactly one or zero `Module`, depending on whether
3//! there is or is not any zig source code, respectively.
4
5const std = @import("std");
6const builtin = @import("builtin");
7const mem = std.mem;
8const Allocator = std.mem.Allocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const assert = std.debug.assert;
11const log = std.log.scoped(.module);
12const BigIntConst = std.math.big.int.Const;
13const BigIntMutable = std.math.big.int.Mutable;
14const Target = std.Target;
15const Ast = std.zig.Ast;
16
17/// Deprecated, use `Zcu`.
18const Module = Zcu;
19const Zcu = @This();
20const Compilation = @import("Compilation.zig");
21const Cache = std.Build.Cache;
22const Value = @import("Value.zig");
23const Type = @import("type.zig").Type;
24const Package = @import("Package.zig");
25const link = @import("link.zig");
26const Air = @import("Air.zig");
27const Zir = std.zig.Zir;
28const trace = @import("tracy.zig").trace;
29const AstGen = std.zig.AstGen;
30const Sema = @import("Sema.zig");
31const target_util = @import("target.zig");
32const build_options = @import("build_options");
33const Liveness = @import("Liveness.zig");
34const isUpDir = @import("introspect.zig").isUpDir;
35const clang = @import("clang.zig");
36const InternPool = @import("InternPool.zig");
37const Alignment = InternPool.Alignment;
38const BuiltinFn = std.zig.BuiltinFn;
39const LlvmObject = @import("codegen/llvm.zig").Object;
40
41comptime {
42 @setEvalBranchQuota(4000);
43 for (
44 @typeInfo(Zir.Inst.Ref).Enum.fields,
45 @typeInfo(Air.Inst.Ref).Enum.fields,
46 @typeInfo(InternPool.Index).Enum.fields,
47 ) |zir_field, air_field, ip_field| {
48 assert(mem.eql(u8, zir_field.name, ip_field.name));
49 assert(mem.eql(u8, air_field.name, ip_field.name));
50 }
51}
52
53/// General-purpose allocator. Used for both temporary and long-term storage.
54gpa: Allocator,
55comp: *Compilation,
56/// Usually, the LlvmObject is managed by linker code, however, in the case
57/// that -fno-emit-bin is specified, the linker code never executes, so we
58/// store the LlvmObject here.
59llvm_object: ?*LlvmObject,
60
61/// Pointer to externally managed resource.
62root_mod: *Package.Module,
63/// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which
64/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
65main_mod: *Package.Module,
66std_mod: *Package.Module,
67sema_prog_node: std.Progress.Node = undefined,
68codegen_prog_node: std.Progress.Node = undefined,
69
70/// Used by AstGen worker to load and store ZIR cache.
71global_zir_cache: Compilation.Directory,
72/// Used by AstGen worker to load and store ZIR cache.
73local_zir_cache: Compilation.Directory,
74/// It's rare for a decl to be exported, so we save memory by having a sparse
75/// map of Decl indexes to details about them being exported.
76/// The Export memory is owned by the `export_owners` table; the slice itself
77/// is owned by this table. The slice is guaranteed to not be empty.
78decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
79/// Same as `decl_exports` but for exported constant values.
80value_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, ArrayListUnmanaged(*Export)) = .{},
81/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
82/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
83/// is performing the export of another Decl.
84/// This table owns the Export memory.
85export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
86/// The set of all the Zig source files in the Module. We keep track of this in order
87/// to iterate over it and check which source files have been modified on the file system when
88/// an update is requested, as well as to cache `@import` results.
89/// Keys are fully resolved file paths. This table owns the keys and values.
90import_table: std.StringArrayHashMapUnmanaged(*File) = .{},
91/// This acts as a map from `path_digest` to the corresponding `File`.
92/// The value is omitted, as keys are ordered identically to `import_table`.
93path_digest_map: std.AutoArrayHashMapUnmanaged(Cache.BinDigest, void) = .{},
94/// The set of all the files which have been loaded with `@embedFile` in the Module.
95/// We keep track of this in order to iterate over it and check which files have been
96/// modified on the file system when an update is requested, as well as to cache
97/// `@embedFile` results.
98/// Keys are fully resolved file paths. This table owns the keys and values.
99embed_table: std.StringArrayHashMapUnmanaged(*EmbedFile) = .{},
100
101/// Stores all Type and Value objects.
102/// The idea is that this will be periodically garbage-collected, but such logic
103/// is not yet implemented.
104intern_pool: InternPool = .{},
105
106/// We optimize memory usage for a compilation with no compile errors by storing the
107/// error messages and mapping outside of `Decl`.
108/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
109/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
110/// a Decl can have a failed_decls entry but have analysis status of success.
111failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{},
112/// Keep track of one `@compileLog` callsite per owner Decl.
113/// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`.
114compile_log_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, extern struct {
115 base_node_inst: InternPool.TrackedInst.Index,
116 node_offset: i32,
117 pub fn src(self: @This()) LazySrcLoc {
118 return .{
119 .base_node_inst = self.base_node_inst,
120 .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset),
121 };
122 }
123}) = .{},
124/// Using a map here for consistency with the other fields here.
125/// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator.
126failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{},
127/// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator.
128failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{},
129/// Using a map here for consistency with the other fields here.
130/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
131failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
132/// If a decl failed due to a cimport error, the corresponding Clang errors
133/// are stored here.
134cimport_errors: std.AutoArrayHashMapUnmanaged(Decl.Index, std.zig.ErrorBundle) = .{},
135
136/// Key is the error name, index is the error tag value. Index 0 has a length-0 string.
137global_error_set: GlobalErrorSet = .{},
138
139/// Maximum amount of distinct error values, set by --error-limit
140error_limit: ErrorInt,
141
142/// Value is the number of PO or outdated Decls which this Depender depends on.
143potentially_outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{},
144/// Value is the number of PO or outdated Decls which this Depender depends on.
145/// Once this value drops to 0, the Depender is a candidate for re-analysis.
146outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{},
147/// This contains all `Depender`s in `outdated` whose PO dependency count is 0.
148/// Such `Depender`s are ready for immediate re-analysis.
149/// See `findOutdatedToAnalyze` for details.
150outdated_ready: std.AutoArrayHashMapUnmanaged(InternPool.Depender, void) = .{},
151/// This contains a set of Decls which may not be in `outdated`, but are the
152/// root Decls of files which have updated source and thus must be re-analyzed.
153/// If such a Decl is only in this set, the struct type index may be preserved
154/// (only the namespace might change). If such a Decl is also `outdated`, the
155/// struct type index must be recreated.
156outdated_file_root: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
157/// This contains a list of Dependers whose analysis or codegen failed, but the
158/// failure was something like running out of disk space, and trying again may
159/// succeed. On the next update, we will flush this list, marking all members of
160/// it as outdated.
161retryable_failures: std.ArrayListUnmanaged(InternPool.Depender) = .{},
162
163stage1_flags: packed struct {
164 have_winmain: bool = false,
165 have_wwinmain: bool = false,
166 have_winmain_crt_startup: bool = false,
167 have_wwinmain_crt_startup: bool = false,
168 have_dllmain_crt_startup: bool = false,
169 have_c_main: bool = false,
170 reserved: u2 = 0,
171} = .{},
172
173compile_log_text: ArrayListUnmanaged(u8) = .{},
174
175emit_h: ?*GlobalEmitH,
176
177test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
178
179global_assembly: std.AutoArrayHashMapUnmanaged(Decl.Index, []u8) = .{},
180
181reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {
182 referencer: Decl.Index,
183 src: LazySrcLoc,
184}) = .{},
185
186panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len,
187/// The panic function body.
188panic_func_index: InternPool.Index = .none,
189null_stack_trace: InternPool.Index = .none,
190
191pub const PanicId = enum {
192 unreach,
193 unwrap_null,
194 cast_to_null,
195 incorrect_alignment,
196 invalid_error_code,
197 cast_truncated_data,
198 negative_to_unsigned,
199 integer_overflow,
200 shl_overflow,
201 shr_overflow,
202 divide_by_zero,
203 exact_division_remainder,
204 inactive_union_field,
205 integer_part_out_of_bounds,
206 corrupt_switch,
207 shift_rhs_too_big,
208 invalid_enum_value,
209 sentinel_mismatch,
210 unwrap_error,
211 index_out_of_bounds,
212 start_index_greater_than_end,
213 for_len_mismatch,
214 memcpy_len_mismatch,
215 memcpy_alias,
216 noreturn_returned,
217
218 pub const len = @typeInfo(PanicId).Enum.fields.len;
219};
220
221pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
222
223pub const CImportError = struct {
224 offset: u32,
225 line: u32,
226 column: u32,
227 path: ?[*:0]u8,
228 source_line: ?[*:0]u8,
229 msg: [*:0]u8,
230
231 pub fn deinit(err: CImportError, gpa: Allocator) void {
232 if (err.path) |some| gpa.free(std.mem.span(some));
233 if (err.source_line) |some| gpa.free(std.mem.span(some));
234 gpa.free(std.mem.span(err.msg));
235 }
236};
237
238/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
239pub const GlobalEmitH = struct {
240 /// Where to put the output.
241 loc: Compilation.EmitLoc,
242 /// When emit_h is non-null, each Decl gets one more compile error slot for
243 /// emit-h failing for that Decl. This table is also how we tell if a Decl has
244 /// failed emit-h or succeeded.
245 failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{},
246 /// Tracks all decls in order to iterate over them and emit .h code for them.
247 decl_table: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
248 /// Similar to the allocated_decls field of Module, this is where `EmitH` objects
249 /// are allocated. There will be exactly one EmitH object per Decl object, with
250 /// identical indexes.
251 allocated_emit_h: std.SegmentedList(EmitH, 0) = .{},
252
253 pub fn declPtr(global_emit_h: *GlobalEmitH, decl_index: Decl.Index) *EmitH {
254 return global_emit_h.allocated_emit_h.at(@intFromEnum(decl_index));
255 }
256};
257
258pub const ErrorInt = u32;
259
260pub const Exported = union(enum) {
261 /// The Decl being exported. Note this is *not* the Decl performing the export.
262 decl_index: Decl.Index,
263 /// Constant value being exported.
264 value: InternPool.Index,
265};
266
267pub const Export = struct {
268 opts: Options,
269 src: LazySrcLoc,
270 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
271 owner_decl: Decl.Index,
272 exported: Exported,
273 status: enum {
274 in_progress,
275 failed,
276 /// Indicates that the failure was due to a temporary issue, such as an I/O error
277 /// when writing to the output file. Retrying the export may succeed.
278 failed_retryable,
279 complete,
280 },
281
282 pub const Options = struct {
283 name: InternPool.NullTerminatedString,
284 linkage: std.builtin.GlobalLinkage = .strong,
285 section: InternPool.OptionalNullTerminatedString = .none,
286 visibility: std.builtin.SymbolVisibility = .default,
287 };
288
289 pub fn getSrcLoc(exp: Export, mod: *Module) SrcLoc {
290 return exp.src.upgrade(mod);
291 }
292};
293
294const ValueArena = struct {
295 state: std.heap.ArenaAllocator.State,
296 state_acquired: ?*std.heap.ArenaAllocator.State = null,
297
298 /// If this ValueArena replaced an existing one during re-analysis, this is the previous instance
299 prev: ?*ValueArena = null,
300
301 /// Returns an allocator backed by either promoting `state`, or by the existing ArenaAllocator
302 /// that has already promoted `state`. `out_arena_allocator` provides storage for the initial promotion,
303 /// and must live until the matching call to release().
304 pub fn acquire(self: *ValueArena, child_allocator: Allocator, out_arena_allocator: *std.heap.ArenaAllocator) Allocator {
305 if (self.state_acquired) |state_acquired| {
306 return @as(*std.heap.ArenaAllocator, @fieldParentPtr("state", state_acquired)).allocator();
307 }
308
309 out_arena_allocator.* = self.state.promote(child_allocator);
310 self.state_acquired = &out_arena_allocator.state;
311 return out_arena_allocator.allocator();
312 }
313
314 /// Releases the allocator acquired by `acquire. `arena_allocator` must match the one passed to `acquire`.
315 pub fn release(self: *ValueArena, arena_allocator: *std.heap.ArenaAllocator) void {
316 if (@as(*std.heap.ArenaAllocator, @fieldParentPtr("state", self.state_acquired.?)) == arena_allocator) {
317 self.state = self.state_acquired.?.*;
318 self.state_acquired = null;
319 }
320 }
321
322 pub fn deinit(self: ValueArena, child_allocator: Allocator) void {
323 assert(self.state_acquired == null);
324
325 const prev = self.prev;
326 self.state.promote(child_allocator).deinit();
327
328 if (prev) |p| {
329 p.deinit(child_allocator);
330 }
331 }
332};
333
334pub const Decl = struct {
335 name: InternPool.NullTerminatedString,
336 /// The most recent Value of the Decl after a successful semantic analysis.
337 /// Populated when `has_tv`.
338 val: Value,
339 /// Populated when `has_tv`.
340 @"linksection": InternPool.OptionalNullTerminatedString,
341 /// Populated when `has_tv`.
342 alignment: Alignment,
343 /// Populated when `has_tv`.
344 @"addrspace": std.builtin.AddressSpace,
345 /// The direct parent namespace of the Decl. In the case of the Decl
346 /// corresponding to a file, this is the namespace of the struct, since
347 /// there is no parent.
348 src_namespace: Namespace.Index,
349
350 /// Line number corresponding to `src_node`. Stored separately so that source files
351 /// do not need to be loaded into memory in order to compute debug line numbers.
352 /// This value is absolute.
353 src_line: u32,
354 /// Index of the ZIR `declaration` instruction from which this `Decl` was created.
355 /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`.
356 zir_decl_index: InternPool.TrackedInst.Index.Optional,
357
358 /// Represents the "shallow" analysis status. For example, for decls that are functions,
359 /// the function type is analyzed with this set to `in_progress`, however, the semantic
360 /// analysis of the function body is performed with this value set to `success`. Functions
361 /// have their own analysis status field.
362 analysis: enum {
363 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
364 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
365 unreferenced,
366 /// Semantic analysis for this Decl is running right now.
367 /// This state detects dependency loops.
368 in_progress,
369 /// The file corresponding to this Decl had a parse error or ZIR error.
370 /// There will be a corresponding ErrorMsg in Zcu.failed_files.
371 file_failure,
372 /// This Decl might be OK but it depends on another one which did not
373 /// successfully complete semantic analysis.
374 dependency_failure,
375 /// Semantic analysis failure.
376 /// There will be a corresponding ErrorMsg in Zcu.failed_decls.
377 sema_failure,
378 /// There will be a corresponding ErrorMsg in Zcu.failed_decls.
379 codegen_failure,
380 /// Sematic analysis and constant value codegen of this Decl has
381 /// succeeded. However, the Decl may be outdated due to an in-progress
382 /// update. Note that for a function, this does not mean codegen of the
383 /// function body succeded: that state is indicated by the function's
384 /// `analysis` field.
385 complete,
386 },
387 /// Whether `typed_value`, `align`, `linksection` and `addrspace` are populated.
388 has_tv: bool,
389 /// If `true` it means the `Decl` is the resource owner of the type/value associated
390 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
391 /// check if the value owns a `Namespace`, and destroy that too.
392 owns_tv: bool,
393 /// Whether the corresponding AST decl has a `pub` keyword.
394 is_pub: bool,
395 /// Whether the corresponding AST decl has a `export` keyword.
396 is_exported: bool,
397 /// If true `name` is already fully qualified.
398 name_fully_qualified: bool = false,
399 /// What kind of a declaration is this.
400 kind: Kind,
401
402 pub const Kind = enum {
403 @"usingnamespace",
404 @"test",
405 @"comptime",
406 named,
407 anon,
408 };
409
410 const Index = InternPool.DeclIndex;
411 const OptionalIndex = InternPool.OptionalDeclIndex;
412
413 pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies {
414 const zir = decl.getFileScope(zcu).zir;
415 const zir_index = decl.zir_decl_index.unwrap().?.resolve(&zcu.intern_pool);
416 const declaration = zir.instructions.items(.data)[@intFromEnum(zir_index)].declaration;
417 const extra = zir.extraData(Zir.Inst.Declaration, declaration.payload_index);
418 return extra.data.getBodies(@intCast(extra.end), zir);
419 }
420
421 pub fn renderFullyQualifiedName(decl: Decl, zcu: *Zcu, writer: anytype) !void {
422 if (decl.name_fully_qualified) {
423 try writer.print("{}", .{decl.name.fmt(&zcu.intern_pool)});
424 } else {
425 try zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedName(zcu, decl.name, writer);
426 }
427 }
428
429 pub fn renderFullyQualifiedDebugName(decl: Decl, zcu: *Zcu, writer: anytype) !void {
430 return zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedDebugName(zcu, decl.name, writer);
431 }
432
433 pub fn fullyQualifiedName(decl: Decl, zcu: *Zcu) !InternPool.NullTerminatedString {
434 return if (decl.name_fully_qualified)
435 decl.name
436 else
437 zcu.namespacePtr(decl.src_namespace).fullyQualifiedName(zcu, decl.name);
438 }
439
440 pub fn typeOf(decl: Decl, zcu: *const Zcu) Type {
441 assert(decl.has_tv);
442 return decl.val.typeOf(zcu);
443 }
444
445 /// Small wrapper for Sema to use over direct access to the `val` field.
446 /// If the value is not populated, instead returns `error.AnalysisFail`.
447 pub fn valueOrFail(decl: Decl) error{AnalysisFail}!Value {
448 if (!decl.has_tv) return error.AnalysisFail;
449 return decl.val;
450 }
451
452 pub fn getOwnedFunction(decl: Decl, zcu: *Zcu) ?InternPool.Key.Func {
453 const i = decl.getOwnedFunctionIndex();
454 if (i == .none) return null;
455 return switch (zcu.intern_pool.indexToKey(i)) {
456 .func => |func| func,
457 else => null,
458 };
459 }
460
461 /// This returns an InternPool.Index even when the value is not a function.
462 pub fn getOwnedFunctionIndex(decl: Decl) InternPool.Index {
463 return if (decl.owns_tv) decl.val.toIntern() else .none;
464 }
465
466 /// If the Decl owns its value and it is an extern function, returns it,
467 /// otherwise null.
468 pub fn getOwnedExternFunc(decl: Decl, zcu: *Zcu) ?InternPool.Key.ExternFunc {
469 return if (decl.owns_tv) decl.val.getExternFunc(zcu) else null;
470 }
471
472 /// If the Decl owns its value and it is a variable, returns it,
473 /// otherwise null.
474 pub fn getOwnedVariable(decl: Decl, zcu: *Zcu) ?InternPool.Key.Variable {
475 return if (decl.owns_tv) decl.val.getVariable(zcu) else null;
476 }
477
478 /// Gets the namespace that this Decl creates by being a struct, union,
479 /// enum, or opaque.
480 pub fn getInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex {
481 if (!decl.has_tv) return .none;
482 const ip = &zcu.intern_pool;
483 return switch (decl.val.ip_index) {
484 .empty_struct_type => .none,
485 .none => .none,
486 else => switch (ip.indexToKey(decl.val.toIntern())) {
487 .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace,
488 .struct_type => ip.loadStructType(decl.val.toIntern()).namespace,
489 .union_type => ip.loadUnionType(decl.val.toIntern()).namespace,
490 .enum_type => ip.loadEnumType(decl.val.toIntern()).namespace,
491 else => .none,
492 },
493 };
494 }
495
496 /// Like `getInnerNamespaceIndex`, but only returns it if the Decl is the owner.
497 pub fn getOwnedInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex {
498 if (!decl.owns_tv) return .none;
499 return decl.getInnerNamespaceIndex(zcu);
500 }
501
502 /// Same as `getOwnedInnerNamespaceIndex` but additionally obtains the pointer.
503 pub fn getOwnedInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace {
504 return zcu.namespacePtrUnwrap(decl.getOwnedInnerNamespaceIndex(zcu));
505 }
506
507 /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer.
508 pub fn getInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace {
509 return zcu.namespacePtrUnwrap(decl.getInnerNamespaceIndex(zcu));
510 }
511
512 pub fn getFileScope(decl: Decl, zcu: *Zcu) *File {
513 return zcu.namespacePtr(decl.src_namespace).file_scope;
514 }
515
516 pub fn getExternDecl(decl: Decl, zcu: *Zcu) OptionalIndex {
517 assert(decl.has_tv);
518 return switch (zcu.intern_pool.indexToKey(decl.val.toIntern())) {
519 .variable => |variable| if (variable.is_extern) variable.decl.toOptional() else .none,
520 .extern_func => |extern_func| extern_func.decl.toOptional(),
521 else => .none,
522 };
523 }
524
525 pub fn isExtern(decl: Decl, zcu: *Zcu) bool {
526 return decl.getExternDecl(zcu) != .none;
527 }
528
529 pub fn getAlignment(decl: Decl, zcu: *Zcu) Alignment {
530 assert(decl.has_tv);
531 if (decl.alignment != .none) return decl.alignment;
532 return decl.typeOf(zcu).abiAlignment(zcu);
533 }
534
535 pub fn declPtrType(decl: Decl, zcu: *Zcu) !Type {
536 assert(decl.has_tv);
537 const decl_ty = decl.typeOf(zcu);
538 return zcu.ptrType(.{
539 .child = decl_ty.toIntern(),
540 .flags = .{
541 .alignment = if (decl.alignment == decl_ty.abiAlignment(zcu))
542 .none
543 else
544 decl.alignment,
545 .address_space = decl.@"addrspace",
546 .is_const = decl.getOwnedVariable(zcu) == null,
547 },
548 });
549 }
550
551 /// Returns the source location of this `Decl`.
552 /// Asserts that this `Decl` corresponds to what will in future be a `Nav` (Named
553 /// Addressable Value): a source-level declaration or generic instantiation.
554 pub fn navSrcLoc(decl: Decl, zcu: *Zcu) LazySrcLoc {
555 return .{
556 .base_node_inst = decl.zir_decl_index.unwrap() orelse inst: {
557 // generic instantiation
558 assert(decl.has_tv);
559 assert(decl.owns_tv);
560 const owner = zcu.funcInfo(decl.val.toIntern()).generic_owner;
561 const generic_owner_decl = zcu.declPtr(zcu.funcInfo(owner).owner_decl);
562 break :inst generic_owner_decl.zir_decl_index.unwrap().?;
563 },
564 .offset = LazySrcLoc.Offset.nodeOffset(0),
565 };
566 }
567};
568
569/// This state is attached to every Decl when Module emit_h is non-null.
570pub const EmitH = struct {
571 fwd_decl: ArrayListUnmanaged(u8) = .{},
572};
573
574pub const DeclAdapter = struct {
575 zcu: *Zcu,
576
577 pub fn hash(self: @This(), s: InternPool.NullTerminatedString) u32 {
578 _ = self;
579 return std.hash.uint32(@intFromEnum(s));
580 }
581
582 pub fn eql(self: @This(), a: InternPool.NullTerminatedString, b_decl_index: Decl.Index, b_index: usize) bool {
583 _ = b_index;
584 return a == self.zcu.declPtr(b_decl_index).name;
585 }
586};
587
588/// The container that structs, enums, unions, and opaques have.
589pub const Namespace = struct {
590 parent: OptionalIndex,
591 file_scope: *File,
592 /// Will be a struct, enum, union, or opaque.
593 decl_index: Decl.Index,
594 /// Direct children of the namespace.
595 /// Declaration order is preserved via entry order.
596 /// These are only declarations named directly by the AST; anonymous
597 /// declarations are not stored here.
598 decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{},
599 /// Key is usingnamespace Decl itself. To find the namespace being included,
600 /// the Decl Value has to be resolved as a Type which has a Namespace.
601 /// Value is whether the usingnamespace decl is marked `pub`.
602 usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{},
603
604 const Index = InternPool.NamespaceIndex;
605 const OptionalIndex = InternPool.OptionalNamespaceIndex;
606
607 const DeclContext = struct {
608 zcu: *Zcu,
609
610 pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 {
611 const decl = ctx.zcu.declPtr(decl_index);
612 return std.hash.uint32(@intFromEnum(decl.name));
613 }
614
615 pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool {
616 _ = b_index;
617 const a_decl = ctx.zcu.declPtr(a_decl_index);
618 const b_decl = ctx.zcu.declPtr(b_decl_index);
619 return a_decl.name == b_decl.name;
620 }
621 };
622
623 // This renders e.g. "std.fs.Dir.OpenOptions"
624 pub fn renderFullyQualifiedName(
625 ns: Namespace,
626 zcu: *Zcu,
627 name: InternPool.NullTerminatedString,
628 writer: anytype,
629 ) @TypeOf(writer).Error!void {
630 if (ns.parent.unwrap()) |parent| {
631 try zcu.namespacePtr(parent).renderFullyQualifiedName(
632 zcu,
633 zcu.declPtr(ns.decl_index).name,
634 writer,
635 );
636 } else {
637 try ns.file_scope.renderFullyQualifiedName(writer);
638 }
639 if (name != .empty) try writer.print(".{}", .{name.fmt(&zcu.intern_pool)});
640 }
641
642 /// This renders e.g. "std/fs.zig:Dir.OpenOptions"
643 pub fn renderFullyQualifiedDebugName(
644 ns: Namespace,
645 zcu: *Zcu,
646 name: InternPool.NullTerminatedString,
647 writer: anytype,
648 ) @TypeOf(writer).Error!void {
649 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: {
650 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName(
651 zcu,
652 zcu.declPtr(ns.decl_index).name,
653 writer,
654 );
655 break :sep '.';
656 } else sep: {
657 try ns.file_scope.renderFullyQualifiedDebugName(writer);
658 break :sep ':';
659 };
660 if (name != .empty) try writer.print("{c}{}", .{ sep, name.fmt(&zcu.intern_pool) });
661 }
662
663 pub fn fullyQualifiedName(
664 ns: Namespace,
665 zcu: *Zcu,
666 name: InternPool.NullTerminatedString,
667 ) !InternPool.NullTerminatedString {
668 const ip = &zcu.intern_pool;
669 const count = count: {
670 var count: usize = name.length(ip) + 1;
671 var cur_ns = &ns;
672 while (true) {
673 const decl = zcu.declPtr(cur_ns.decl_index);
674 count += decl.name.length(ip) + 1;
675 cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse {
676 count += ns.file_scope.sub_file_path.len;
677 break :count count;
678 });
679 }
680 };
681
682 const gpa = zcu.gpa;
683 const start = ip.string_bytes.items.len;
684 // Protects reads of interned strings from being reallocated during the call to
685 // renderFullyQualifiedName.
686 try ip.string_bytes.ensureUnusedCapacity(gpa, count);
687 ns.renderFullyQualifiedName(zcu, name, ip.string_bytes.writer(gpa)) catch unreachable;
688
689 // Sanitize the name for nvptx which is more restrictive.
690 // TODO This should be handled by the backend, not the frontend. Have a
691 // look at how the C backend does it for inspiration.
692 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;
693 if (cpu_arch.isNvptx()) {
694 for (ip.string_bytes.items[start..]) |*byte| switch (byte.*) {
695 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_',
696 else => {},
697 };
698 }
699
700 return ip.getOrPutTrailingString(gpa, ip.string_bytes.items.len - start, .no_embedded_nulls);
701 }
702
703 pub fn getType(ns: Namespace, zcu: *Zcu) Type {
704 const decl = zcu.declPtr(ns.decl_index);
705 assert(decl.has_tv);
706 return decl.val.toType();
707 }
708};
709
710pub const File = struct {
711 /// The Decl of the struct that represents this File.
712 root_decl: Decl.OptionalIndex,
713 status: enum {
714 never_loaded,
715 retryable_failure,
716 parse_failure,
717 astgen_failure,
718 success_zir,
719 },
720 source_loaded: bool,
721 tree_loaded: bool,
722 zir_loaded: bool,
723 /// Relative to the owning package's root_src_dir.
724 /// Memory is stored in gpa, owned by File.
725 sub_file_path: []const u8,
726 /// Whether this is populated depends on `source_loaded`.
727 source: [:0]const u8,
728 /// Whether this is populated depends on `status`.
729 stat: Cache.File.Stat,
730 /// Whether this is populated or not depends on `tree_loaded`.
731 tree: Ast,
732 /// Whether this is populated or not depends on `zir_loaded`.
733 zir: Zir,
734 /// Module that this file is a part of, managed externally.
735 mod: *Package.Module,
736 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
737 multi_pkg: bool = false,
738 /// List of references to this file, used for multi-package errors.
739 references: std.ArrayListUnmanaged(Reference) = .{},
740 /// The hash of the path to this file, used to store `InternPool.TrackedInst`.
741 path_digest: Cache.BinDigest,
742
743 /// The most recent successful ZIR for this file, with no errors.
744 /// This is only populated when a previously successful ZIR
745 /// newly introduces compile errors during an update. When ZIR is
746 /// successful, this field is unloaded.
747 prev_zir: ?*Zir = null,
748
749 /// A single reference to a file.
750 pub const Reference = union(enum) {
751 /// The file is imported directly (i.e. not as a package) with @import.
752 import: SrcLoc,
753 /// The file is the root of a module.
754 root: *Package.Module,
755 };
756
757 pub fn unload(file: *File, gpa: Allocator) void {
758 file.unloadTree(gpa);
759 file.unloadSource(gpa);
760 file.unloadZir(gpa);
761 }
762
763 pub fn unloadTree(file: *File, gpa: Allocator) void {
764 if (file.tree_loaded) {
765 file.tree_loaded = false;
766 file.tree.deinit(gpa);
767 }
768 }
769
770 pub fn unloadSource(file: *File, gpa: Allocator) void {
771 if (file.source_loaded) {
772 file.source_loaded = false;
773 gpa.free(file.source);
774 }
775 }
776
777 pub fn unloadZir(file: *File, gpa: Allocator) void {
778 if (file.zir_loaded) {
779 file.zir_loaded = false;
780 file.zir.deinit(gpa);
781 }
782 }
783
784 pub fn deinit(file: *File, mod: *Module) void {
785 const gpa = mod.gpa;
786 const is_builtin = file.mod.isBuiltin();
787 log.debug("deinit File {s}", .{file.sub_file_path});
788 if (is_builtin) {
789 file.unloadTree(gpa);
790 file.unloadZir(gpa);
791 } else {
792 gpa.free(file.sub_file_path);
793 file.unload(gpa);
794 }
795 file.references.deinit(gpa);
796 if (file.root_decl.unwrap()) |root_decl| {
797 mod.destroyDecl(root_decl);
798 }
799 if (file.prev_zir) |prev_zir| {
800 prev_zir.deinit(gpa);
801 gpa.destroy(prev_zir);
802 }
803 file.* = undefined;
804 }
805
806 pub const Source = struct {
807 bytes: [:0]const u8,
808 stat: Cache.File.Stat,
809 };
810
811 pub fn getSource(file: *File, gpa: Allocator) !Source {
812 if (file.source_loaded) return Source{
813 .bytes = file.source,
814 .stat = file.stat,
815 };
816
817 // Keep track of inode, file size, mtime, hash so we can detect which files
818 // have been modified when an incremental update is requested.
819 var f = try file.mod.root.openFile(file.sub_file_path, .{});
820 defer f.close();
821
822 const stat = try f.stat();
823
824 if (stat.size > std.math.maxInt(u32))
825 return error.FileTooBig;
826
827 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
828 defer if (!file.source_loaded) gpa.free(source);
829 const amt = try f.readAll(source);
830 if (amt != stat.size)
831 return error.UnexpectedEndOfFile;
832
833 // Here we do not modify stat fields because this function is the one
834 // used for error reporting. We need to keep the stat fields stale so that
835 // astGenFile can know to regenerate ZIR.
836
837 file.source = source;
838 file.source_loaded = true;
839 return Source{
840 .bytes = source,
841 .stat = .{
842 .size = stat.size,
843 .inode = stat.inode,
844 .mtime = stat.mtime,
845 },
846 };
847 }
848
849 pub fn getTree(file: *File, gpa: Allocator) !*const Ast {
850 if (file.tree_loaded) return &file.tree;
851
852 const source = try file.getSource(gpa);
853 file.tree = try Ast.parse(gpa, source.bytes, .zig);
854 file.tree_loaded = true;
855 return &file.tree;
856 }
857
858 pub fn destroy(file: *File, mod: *Module) void {
859 const gpa = mod.gpa;
860 const is_builtin = file.mod.isBuiltin();
861 file.deinit(mod);
862 if (!is_builtin) gpa.destroy(file);
863 }
864
865 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {
866 // Convert all the slashes into dots and truncate the extension.
867 const ext = std.fs.path.extension(file.sub_file_path);
868 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
869 for (noext) |byte| switch (byte) {
870 '/', '\\' => try writer.writeByte('.'),
871 else => try writer.writeByte(byte),
872 };
873 }
874
875 pub fn renderFullyQualifiedDebugName(file: File, writer: anytype) !void {
876 for (file.sub_file_path) |byte| switch (byte) {
877 '/', '\\' => try writer.writeByte('/'),
878 else => try writer.writeByte(byte),
879 };
880 }
881
882 pub fn fullyQualifiedName(file: File, mod: *Module) !InternPool.NullTerminatedString {
883 const ip = &mod.intern_pool;
884 const start = ip.string_bytes.items.len;
885 try file.renderFullyQualifiedName(ip.string_bytes.writer(mod.gpa));
886 return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start, .no_embedded_nulls);
887 }
888
889 pub fn fullPath(file: File, ally: Allocator) ![]u8 {
890 return file.mod.root.joinString(ally, file.sub_file_path);
891 }
892
893 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
894 const loc = std.zig.findLineColumn(file.source.bytes, src);
895 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
896 }
897
898 pub fn okToReportErrors(file: File) bool {
899 return switch (file.status) {
900 .parse_failure, .astgen_failure => false,
901 else => true,
902 };
903 }
904
905 /// Add a reference to this file during AstGen.
906 pub fn addReference(file: *File, mod: Module, ref: Reference) !void {
907 // Don't add the same module root twice. Note that since we always add module roots at the
908 // front of the references array (see below), this loop is actually O(1) on valid code.
909 if (ref == .root) {
910 for (file.references.items) |other| {
911 switch (other) {
912 .root => |r| if (ref.root == r) return,
913 else => break, // reached the end of the "is-root" references
914 }
915 }
916 }
917
918 switch (ref) {
919 // We put root references at the front of the list both to make the above loop fast and
920 // to make multi-module errors more helpful (since "root-of" notes are generally more
921 // informative than "imported-from" notes). This path is hit very rarely, so the speed
922 // of the insert operation doesn't matter too much.
923 .root => try file.references.insert(mod.gpa, 0, ref),
924
925 // Other references we'll just put at the end.
926 else => try file.references.append(mod.gpa, ref),
927 }
928
929 const pkg = switch (ref) {
930 .import => |loc| loc.file_scope.mod,
931 .root => |pkg| pkg,
932 };
933 if (pkg != file.mod) file.multi_pkg = true;
934 }
935
936 /// Mark this file and every file referenced by it as multi_pkg and report an
937 /// astgen_failure error for them. AstGen must have completed in its entirety.
938 pub fn recursiveMarkMultiPkg(file: *File, mod: *Module) void {
939 file.multi_pkg = true;
940 file.status = .astgen_failure;
941
942 // We can only mark children as failed if the ZIR is loaded, which may not
943 // be the case if there were other astgen failures in this file
944 if (!file.zir_loaded) return;
945
946 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
947 if (imports_index == 0) return;
948 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
949
950 var extra_index = extra.end;
951 for (0..extra.data.imports_len) |_| {
952 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
953 extra_index = item.end;
954
955 const import_path = file.zir.nullTerminatedString(item.data.name);
956 if (mem.eql(u8, import_path, "builtin")) continue;
957
958 const res = mod.importFile(file, import_path) catch continue;
959 if (!res.is_pkg and !res.file.multi_pkg) {
960 res.file.recursiveMarkMultiPkg(mod);
961 }
962 }
963 }
964};
965
966pub const EmbedFile = struct {
967 /// Relative to the owning module's root directory.
968 sub_file_path: InternPool.NullTerminatedString,
969 /// Module that this file is a part of, managed externally.
970 owner: *Package.Module,
971 stat: Cache.File.Stat,
972 val: InternPool.Index,
973 src_loc: SrcLoc,
974};
975
976/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
977/// Its memory is managed with the general purpose allocator so that they
978/// can be created and destroyed in response to incremental updates.
979/// In some cases, the File could have been inferred from where the ErrorMsg
980/// is stored. For example, if it is stored in Module.failed_decls, then the File
981/// would be determined by the Decl Scope. However, the data structure contains the field
982/// anyway so that `ErrorMsg` can be reused for error notes, which may be in a different
983/// file than the parent error message. It also simplifies processing of error messages.
984pub const ErrorMsg = struct {
985 src_loc: SrcLoc,
986 msg: []const u8,
987 notes: []ErrorMsg = &.{},
988 reference_trace: []Trace = &.{},
989 hidden_references: u32 = 0,
990
991 pub const Trace = struct {
992 decl: InternPool.NullTerminatedString,
993 src_loc: SrcLoc,
994 };
995
996 pub fn create(
997 gpa: Allocator,
998 src_loc: SrcLoc,
999 comptime format: []const u8,
1000 args: anytype,
1001 ) !*ErrorMsg {
1002 assert(src_loc.lazy != .unneeded);
1003 const err_msg = try gpa.create(ErrorMsg);
1004 errdefer gpa.destroy(err_msg);
1005 err_msg.* = try ErrorMsg.init(gpa, src_loc, format, args);
1006 return err_msg;
1007 }
1008
1009 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`,
1010 /// as well as all notes.
1011 pub fn destroy(err_msg: *ErrorMsg, gpa: Allocator) void {
1012 err_msg.deinit(gpa);
1013 gpa.destroy(err_msg);
1014 }
1015
1016 pub fn init(
1017 gpa: Allocator,
1018 src_loc: SrcLoc,
1019 comptime format: []const u8,
1020 args: anytype,
1021 ) !ErrorMsg {
1022 return ErrorMsg{
1023 .src_loc = src_loc,
1024 .msg = try std.fmt.allocPrint(gpa, format, args),
1025 };
1026 }
1027
1028 pub fn deinit(err_msg: *ErrorMsg, gpa: Allocator) void {
1029 for (err_msg.notes) |*note| {
1030 note.deinit(gpa);
1031 }
1032 gpa.free(err_msg.notes);
1033 gpa.free(err_msg.msg);
1034 gpa.free(err_msg.reference_trace);
1035 err_msg.* = undefined;
1036 }
1037};
1038
1039/// Canonical reference to a position within a source file.
1040pub const SrcLoc = struct {
1041 file_scope: *File,
1042 base_node: Ast.Node.Index,
1043 /// Relative to `base_node`.
1044 lazy: LazySrcLoc.Offset,
1045
1046 pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex {
1047 const tree = src_loc.file_scope.tree;
1048 return tree.firstToken(src_loc.base_node);
1049 }
1050
1051 pub fn relativeToNodeIndex(src_loc: SrcLoc, offset: i32) Ast.Node.Index {
1052 return @bitCast(offset + @as(i32, @bitCast(src_loc.base_node)));
1053 }
1054
1055 pub const Span = Ast.Span;
1056
1057 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span {
1058 switch (src_loc.lazy) {
1059 .unneeded => unreachable,
1060 .entire_file => return Span{ .start = 0, .end = 1, .main = 0 },
1061
1062 .byte_abs => |byte_index| return Span{ .start = byte_index, .end = byte_index + 1, .main = byte_index },
1063
1064 .token_abs => |tok_index| {
1065 const tree = try src_loc.file_scope.getTree(gpa);
1066 const start = tree.tokens.items(.start)[tok_index];
1067 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1068 return Span{ .start = start, .end = end, .main = start };
1069 },
1070 .node_abs => |node| {
1071 const tree = try src_loc.file_scope.getTree(gpa);
1072 return tree.nodeToSpan(node);
1073 },
1074 .byte_offset => |byte_off| {
1075 const tree = try src_loc.file_scope.getTree(gpa);
1076 const tok_index = src_loc.baseSrcToken();
1077 const start = tree.tokens.items(.start)[tok_index] + byte_off;
1078 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1079 return Span{ .start = start, .end = end, .main = start };
1080 },
1081 .token_offset => |tok_off| {
1082 const tree = try src_loc.file_scope.getTree(gpa);
1083 const tok_index = src_loc.baseSrcToken() + tok_off;
1084 const start = tree.tokens.items(.start)[tok_index];
1085 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1086 return Span{ .start = start, .end = end, .main = start };
1087 },
1088 .node_offset => |traced_off| {
1089 const node_off = traced_off.x;
1090 const tree = try src_loc.file_scope.getTree(gpa);
1091 const node = src_loc.relativeToNodeIndex(node_off);
1092 assert(src_loc.file_scope.tree_loaded);
1093 return tree.nodeToSpan(node);
1094 },
1095 .node_offset_main_token => |node_off| {
1096 const tree = try src_loc.file_scope.getTree(gpa);
1097 const node = src_loc.relativeToNodeIndex(node_off);
1098 const main_token = tree.nodes.items(.main_token)[node];
1099 return tree.tokensToSpan(main_token, main_token, main_token);
1100 },
1101 .node_offset_bin_op => |node_off| {
1102 const tree = try src_loc.file_scope.getTree(gpa);
1103 const node = src_loc.relativeToNodeIndex(node_off);
1104 assert(src_loc.file_scope.tree_loaded);
1105 return tree.nodeToSpan(node);
1106 },
1107 .node_offset_initializer => |node_off| {
1108 const tree = try src_loc.file_scope.getTree(gpa);
1109 const node = src_loc.relativeToNodeIndex(node_off);
1110 return tree.tokensToSpan(
1111 tree.firstToken(node) - 3,
1112 tree.lastToken(node),
1113 tree.nodes.items(.main_token)[node] - 2,
1114 );
1115 },
1116 .node_offset_var_decl_ty => |node_off| {
1117 const tree = try src_loc.file_scope.getTree(gpa);
1118 const node = src_loc.relativeToNodeIndex(node_off);
1119 const node_tags = tree.nodes.items(.tag);
1120 const full = switch (node_tags[node]) {
1121 .global_var_decl,
1122 .local_var_decl,
1123 .simple_var_decl,
1124 .aligned_var_decl,
1125 => tree.fullVarDecl(node).?,
1126 .@"usingnamespace" => {
1127 const node_data = tree.nodes.items(.data);
1128 return tree.nodeToSpan(node_data[node].lhs);
1129 },
1130 else => unreachable,
1131 };
1132 if (full.ast.type_node != 0) {
1133 return tree.nodeToSpan(full.ast.type_node);
1134 }
1135 const tok_index = full.ast.mut_token + 1; // the name token
1136 const start = tree.tokens.items(.start)[tok_index];
1137 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1138 return Span{ .start = start, .end = end, .main = start };
1139 },
1140 .node_offset_var_decl_align => |node_off| {
1141 const tree = try src_loc.file_scope.getTree(gpa);
1142 const node = src_loc.relativeToNodeIndex(node_off);
1143 const full = tree.fullVarDecl(node).?;
1144 return tree.nodeToSpan(full.ast.align_node);
1145 },
1146 .node_offset_var_decl_section => |node_off| {
1147 const tree = try src_loc.file_scope.getTree(gpa);
1148 const node = src_loc.relativeToNodeIndex(node_off);
1149 const full = tree.fullVarDecl(node).?;
1150 return tree.nodeToSpan(full.ast.section_node);
1151 },
1152 .node_offset_var_decl_addrspace => |node_off| {
1153 const tree = try src_loc.file_scope.getTree(gpa);
1154 const node = src_loc.relativeToNodeIndex(node_off);
1155 const full = tree.fullVarDecl(node).?;
1156 return tree.nodeToSpan(full.ast.addrspace_node);
1157 },
1158 .node_offset_var_decl_init => |node_off| {
1159 const tree = try src_loc.file_scope.getTree(gpa);
1160 const node = src_loc.relativeToNodeIndex(node_off);
1161 const full = tree.fullVarDecl(node).?;
1162 return tree.nodeToSpan(full.ast.init_node);
1163 },
1164 .node_offset_builtin_call_arg => |builtin_arg| {
1165 const tree = try src_loc.file_scope.getTree(gpa);
1166 const node_datas = tree.nodes.items(.data);
1167 const node_tags = tree.nodes.items(.tag);
1168 const node = src_loc.relativeToNodeIndex(builtin_arg.builtin_call_node);
1169 const param = switch (node_tags[node]) {
1170 .builtin_call_two, .builtin_call_two_comma => switch (builtin_arg.arg_index) {
1171 0 => node_datas[node].lhs,
1172 1 => node_datas[node].rhs,
1173 else => unreachable,
1174 },
1175 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + builtin_arg.arg_index],
1176 else => unreachable,
1177 };
1178 return tree.nodeToSpan(param);
1179 },
1180 .node_offset_ptrcast_operand => |node_off| {
1181 const tree = try src_loc.file_scope.getTree(gpa);
1182 const main_tokens = tree.nodes.items(.main_token);
1183 const node_datas = tree.nodes.items(.data);
1184 const node_tags = tree.nodes.items(.tag);
1185
1186 var node = src_loc.relativeToNodeIndex(node_off);
1187 while (true) {
1188 switch (node_tags[node]) {
1189 .builtin_call_two, .builtin_call_two_comma => {},
1190 else => break,
1191 }
1192
1193 if (node_datas[node].lhs == 0) break; // 0 args
1194 if (node_datas[node].rhs != 0) break; // 2 args
1195
1196 const builtin_token = main_tokens[node];
1197 const builtin_name = tree.tokenSlice(builtin_token);
1198 const info = BuiltinFn.list.get(builtin_name) orelse break;
1199
1200 switch (info.tag) {
1201 else => break,
1202 .ptr_cast,
1203 .align_cast,
1204 .addrspace_cast,
1205 .const_cast,
1206 .volatile_cast,
1207 => {},
1208 }
1209
1210 node = node_datas[node].lhs;
1211 }
1212
1213 return tree.nodeToSpan(node);
1214 },
1215 .node_offset_array_access_index => |node_off| {
1216 const tree = try src_loc.file_scope.getTree(gpa);
1217 const node_datas = tree.nodes.items(.data);
1218 const node = src_loc.relativeToNodeIndex(node_off);
1219 return tree.nodeToSpan(node_datas[node].rhs);
1220 },
1221 .node_offset_slice_ptr,
1222 .node_offset_slice_start,
1223 .node_offset_slice_end,
1224 .node_offset_slice_sentinel,
1225 => |node_off| {
1226 const tree = try src_loc.file_scope.getTree(gpa);
1227 const node = src_loc.relativeToNodeIndex(node_off);
1228 const full = tree.fullSlice(node).?;
1229 const part_node = switch (src_loc.lazy) {
1230 .node_offset_slice_ptr => full.ast.sliced,
1231 .node_offset_slice_start => full.ast.start,
1232 .node_offset_slice_end => full.ast.end,
1233 .node_offset_slice_sentinel => full.ast.sentinel,
1234 else => unreachable,
1235 };
1236 return tree.nodeToSpan(part_node);
1237 },
1238 .node_offset_call_func => |node_off| {
1239 const tree = try src_loc.file_scope.getTree(gpa);
1240 const node = src_loc.relativeToNodeIndex(node_off);
1241 var buf: [1]Ast.Node.Index = undefined;
1242 const full = tree.fullCall(&buf, node).?;
1243 return tree.nodeToSpan(full.ast.fn_expr);
1244 },
1245 .node_offset_field_name => |node_off| {
1246 const tree = try src_loc.file_scope.getTree(gpa);
1247 const node_datas = tree.nodes.items(.data);
1248 const node_tags = tree.nodes.items(.tag);
1249 const node = src_loc.relativeToNodeIndex(node_off);
1250 var buf: [1]Ast.Node.Index = undefined;
1251 const tok_index = switch (node_tags[node]) {
1252 .field_access => node_datas[node].rhs,
1253 .call_one,
1254 .call_one_comma,
1255 .async_call_one,
1256 .async_call_one_comma,
1257 .call,
1258 .call_comma,
1259 .async_call,
1260 .async_call_comma,
1261 => blk: {
1262 const full = tree.fullCall(&buf, node).?;
1263 break :blk tree.lastToken(full.ast.fn_expr);
1264 },
1265 else => tree.firstToken(node) - 2,
1266 };
1267 const start = tree.tokens.items(.start)[tok_index];
1268 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1269 return Span{ .start = start, .end = end, .main = start };
1270 },
1271 .node_offset_field_name_init => |node_off| {
1272 const tree = try src_loc.file_scope.getTree(gpa);
1273 const node = src_loc.relativeToNodeIndex(node_off);
1274 const tok_index = tree.firstToken(node) - 2;
1275 const start = tree.tokens.items(.start)[tok_index];
1276 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1277 return Span{ .start = start, .end = end, .main = start };
1278 },
1279 .node_offset_deref_ptr => |node_off| {
1280 const tree = try src_loc.file_scope.getTree(gpa);
1281 const node = src_loc.relativeToNodeIndex(node_off);
1282 return tree.nodeToSpan(node);
1283 },
1284 .node_offset_asm_source => |node_off| {
1285 const tree = try src_loc.file_scope.getTree(gpa);
1286 const node = src_loc.relativeToNodeIndex(node_off);
1287 const full = tree.fullAsm(node).?;
1288 return tree.nodeToSpan(full.ast.template);
1289 },
1290 .node_offset_asm_ret_ty => |node_off| {
1291 const tree = try src_loc.file_scope.getTree(gpa);
1292 const node = src_loc.relativeToNodeIndex(node_off);
1293 const full = tree.fullAsm(node).?;
1294 const asm_output = full.outputs[0];
1295 const node_datas = tree.nodes.items(.data);
1296 return tree.nodeToSpan(node_datas[asm_output].lhs);
1297 },
1298
1299 .node_offset_if_cond => |node_off| {
1300 const tree = try src_loc.file_scope.getTree(gpa);
1301 const node = src_loc.relativeToNodeIndex(node_off);
1302 const node_tags = tree.nodes.items(.tag);
1303 const src_node = switch (node_tags[node]) {
1304 .if_simple,
1305 .@"if",
1306 => tree.fullIf(node).?.ast.cond_expr,
1307
1308 .while_simple,
1309 .while_cont,
1310 .@"while",
1311 => tree.fullWhile(node).?.ast.cond_expr,
1312
1313 .for_simple,
1314 .@"for",
1315 => {
1316 const inputs = tree.fullFor(node).?.ast.inputs;
1317 const start = tree.firstToken(inputs[0]);
1318 const end = tree.lastToken(inputs[inputs.len - 1]);
1319 return tree.tokensToSpan(start, end, start);
1320 },
1321
1322 .@"orelse" => node,
1323 .@"catch" => node,
1324 else => unreachable,
1325 };
1326 return tree.nodeToSpan(src_node);
1327 },
1328 .for_input => |for_input| {
1329 const tree = try src_loc.file_scope.getTree(gpa);
1330 const node = src_loc.relativeToNodeIndex(for_input.for_node_offset);
1331 const for_full = tree.fullFor(node).?;
1332 const src_node = for_full.ast.inputs[for_input.input_index];
1333 return tree.nodeToSpan(src_node);
1334 },
1335 .for_capture_from_input => |node_off| {
1336 const tree = try src_loc.file_scope.getTree(gpa);
1337 const token_tags = tree.tokens.items(.tag);
1338 const input_node = src_loc.relativeToNodeIndex(node_off);
1339 // We have to actually linear scan the whole AST to find the for loop
1340 // that contains this input.
1341 const node_tags = tree.nodes.items(.tag);
1342 for (node_tags, 0..) |node_tag, node_usize| {
1343 const node = @as(Ast.Node.Index, @intCast(node_usize));
1344 switch (node_tag) {
1345 .for_simple, .@"for" => {
1346 const for_full = tree.fullFor(node).?;
1347 for (for_full.ast.inputs, 0..) |input, input_index| {
1348 if (input_node == input) {
1349 var count = input_index;
1350 var tok = for_full.payload_token;
1351 while (true) {
1352 switch (token_tags[tok]) {
1353 .comma => {
1354 count -= 1;
1355 tok += 1;
1356 },
1357 .identifier => {
1358 if (count == 0)
1359 return tree.tokensToSpan(tok, tok + 1, tok);
1360 tok += 1;
1361 },
1362 .asterisk => {
1363 if (count == 0)
1364 return tree.tokensToSpan(tok, tok + 2, tok);
1365 tok += 1;
1366 },
1367 else => unreachable,
1368 }
1369 }
1370 }
1371 }
1372 },
1373 else => continue,
1374 }
1375 } else unreachable;
1376 },
1377 .call_arg => |call_arg| {
1378 const tree = try src_loc.file_scope.getTree(gpa);
1379 const node = src_loc.relativeToNodeIndex(call_arg.call_node_offset);
1380 var buf: [2]Ast.Node.Index = undefined;
1381 const call_full = tree.fullCall(buf[0..1], node) orelse {
1382 const node_tags = tree.nodes.items(.tag);
1383 assert(node_tags[node] == .builtin_call);
1384 const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1];
1385 switch (node_tags[call_args_node]) {
1386 .array_init_one,
1387 .array_init_one_comma,
1388 .array_init_dot_two,
1389 .array_init_dot_two_comma,
1390 .array_init_dot,
1391 .array_init_dot_comma,
1392 .array_init,
1393 .array_init_comma,
1394 => {
1395 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements;
1396 return tree.nodeToSpan(full[call_arg.arg_index]);
1397 },
1398 .struct_init_one,
1399 .struct_init_one_comma,
1400 .struct_init_dot_two,
1401 .struct_init_dot_two_comma,
1402 .struct_init_dot,
1403 .struct_init_dot_comma,
1404 .struct_init,
1405 .struct_init_comma,
1406 => {
1407 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields;
1408 return tree.nodeToSpan(full[call_arg.arg_index]);
1409 },
1410 else => return tree.nodeToSpan(call_args_node),
1411 }
1412 };
1413 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]);
1414 },
1415 .fn_proto_param, .fn_proto_param_type => |fn_proto_param| {
1416 const tree = try src_loc.file_scope.getTree(gpa);
1417 const node = src_loc.relativeToNodeIndex(fn_proto_param.fn_proto_node_offset);
1418 var buf: [1]Ast.Node.Index = undefined;
1419 const full = tree.fullFnProto(&buf, node).?;
1420 var it = full.iterate(tree);
1421 var i: usize = 0;
1422 while (it.next()) |param| : (i += 1) {
1423 if (i != fn_proto_param.param_index) continue;
1424
1425 switch (src_loc.lazy) {
1426 .fn_proto_param_type => if (param.anytype_ellipsis3) |tok| {
1427 return tree.tokenToSpan(tok);
1428 } else {
1429 return tree.nodeToSpan(param.type_expr);
1430 },
1431 .fn_proto_param => if (param.anytype_ellipsis3) |tok| {
1432 const first = param.comptime_noalias orelse param.name_token orelse tok;
1433 return tree.tokensToSpan(first, tok, first);
1434 } else {
1435 const first = param.comptime_noalias orelse param.name_token orelse tree.firstToken(param.type_expr);
1436 return tree.tokensToSpan(first, tree.lastToken(param.type_expr), first);
1437 },
1438 else => unreachable,
1439 }
1440 }
1441 unreachable;
1442 },
1443 .node_offset_bin_lhs => |node_off| {
1444 const tree = try src_loc.file_scope.getTree(gpa);
1445 const node = src_loc.relativeToNodeIndex(node_off);
1446 const node_datas = tree.nodes.items(.data);
1447 return tree.nodeToSpan(node_datas[node].lhs);
1448 },
1449 .node_offset_bin_rhs => |node_off| {
1450 const tree = try src_loc.file_scope.getTree(gpa);
1451 const node = src_loc.relativeToNodeIndex(node_off);
1452 const node_datas = tree.nodes.items(.data);
1453 return tree.nodeToSpan(node_datas[node].rhs);
1454 },
1455 .array_cat_lhs, .array_cat_rhs => |cat| {
1456 const tree = try src_loc.file_scope.getTree(gpa);
1457 const node = src_loc.relativeToNodeIndex(cat.array_cat_offset);
1458 const node_datas = tree.nodes.items(.data);
1459 const arr_node = if (src_loc.lazy == .array_cat_lhs)
1460 node_datas[node].lhs
1461 else
1462 node_datas[node].rhs;
1463
1464 const node_tags = tree.nodes.items(.tag);
1465 var buf: [2]Ast.Node.Index = undefined;
1466 switch (node_tags[arr_node]) {
1467 .array_init_one,
1468 .array_init_one_comma,
1469 .array_init_dot_two,
1470 .array_init_dot_two_comma,
1471 .array_init_dot,
1472 .array_init_dot_comma,
1473 .array_init,
1474 .array_init_comma,
1475 => {
1476 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements;
1477 return tree.nodeToSpan(full[cat.elem_index]);
1478 },
1479 else => return tree.nodeToSpan(arr_node),
1480 }
1481 },
1482
1483 .node_offset_switch_operand => |node_off| {
1484 const tree = try src_loc.file_scope.getTree(gpa);
1485 const node = src_loc.relativeToNodeIndex(node_off);
1486 const node_datas = tree.nodes.items(.data);
1487 return tree.nodeToSpan(node_datas[node].lhs);
1488 },
1489
1490 .node_offset_switch_special_prong => |node_off| {
1491 const tree = try src_loc.file_scope.getTree(gpa);
1492 const switch_node = src_loc.relativeToNodeIndex(node_off);
1493 const node_datas = tree.nodes.items(.data);
1494 const node_tags = tree.nodes.items(.tag);
1495 const main_tokens = tree.nodes.items(.main_token);
1496 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
1497 const case_nodes = tree.extra_data[extra.start..extra.end];
1498 for (case_nodes) |case_node| {
1499 const case = tree.fullSwitchCase(case_node).?;
1500 const is_special = (case.ast.values.len == 0) or
1501 (case.ast.values.len == 1 and
1502 node_tags[case.ast.values[0]] == .identifier and
1503 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1504 if (!is_special) continue;
1505
1506 return tree.nodeToSpan(case_node);
1507 } else unreachable;
1508 },
1509
1510 .node_offset_switch_range => |node_off| {
1511 const tree = try src_loc.file_scope.getTree(gpa);
1512 const switch_node = src_loc.relativeToNodeIndex(node_off);
1513 const node_datas = tree.nodes.items(.data);
1514 const node_tags = tree.nodes.items(.tag);
1515 const main_tokens = tree.nodes.items(.main_token);
1516 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
1517 const case_nodes = tree.extra_data[extra.start..extra.end];
1518 for (case_nodes) |case_node| {
1519 const case = tree.fullSwitchCase(case_node).?;
1520 const is_special = (case.ast.values.len == 0) or
1521 (case.ast.values.len == 1 and
1522 node_tags[case.ast.values[0]] == .identifier and
1523 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1524 if (is_special) continue;
1525
1526 for (case.ast.values) |item_node| {
1527 if (node_tags[item_node] == .switch_range) {
1528 return tree.nodeToSpan(item_node);
1529 }
1530 }
1531 } else unreachable;
1532 },
1533 .node_offset_fn_type_align => |node_off| {
1534 const tree = try src_loc.file_scope.getTree(gpa);
1535 const node = src_loc.relativeToNodeIndex(node_off);
1536 var buf: [1]Ast.Node.Index = undefined;
1537 const full = tree.fullFnProto(&buf, node).?;
1538 return tree.nodeToSpan(full.ast.align_expr);
1539 },
1540 .node_offset_fn_type_addrspace => |node_off| {
1541 const tree = try src_loc.file_scope.getTree(gpa);
1542 const node = src_loc.relativeToNodeIndex(node_off);
1543 var buf: [1]Ast.Node.Index = undefined;
1544 const full = tree.fullFnProto(&buf, node).?;
1545 return tree.nodeToSpan(full.ast.addrspace_expr);
1546 },
1547 .node_offset_fn_type_section => |node_off| {
1548 const tree = try src_loc.file_scope.getTree(gpa);
1549 const node = src_loc.relativeToNodeIndex(node_off);
1550 var buf: [1]Ast.Node.Index = undefined;
1551 const full = tree.fullFnProto(&buf, node).?;
1552 return tree.nodeToSpan(full.ast.section_expr);
1553 },
1554 .node_offset_fn_type_cc => |node_off| {
1555 const tree = try src_loc.file_scope.getTree(gpa);
1556 const node = src_loc.relativeToNodeIndex(node_off);
1557 var buf: [1]Ast.Node.Index = undefined;
1558 const full = tree.fullFnProto(&buf, node).?;
1559 return tree.nodeToSpan(full.ast.callconv_expr);
1560 },
1561
1562 .node_offset_fn_type_ret_ty => |node_off| {
1563 const tree = try src_loc.file_scope.getTree(gpa);
1564 const node = src_loc.relativeToNodeIndex(node_off);
1565 var buf: [1]Ast.Node.Index = undefined;
1566 const full = tree.fullFnProto(&buf, node).?;
1567 return tree.nodeToSpan(full.ast.return_type);
1568 },
1569 .node_offset_param => |node_off| {
1570 const tree = try src_loc.file_scope.getTree(gpa);
1571 const token_tags = tree.tokens.items(.tag);
1572 const node = src_loc.relativeToNodeIndex(node_off);
1573
1574 var first_tok = tree.firstToken(node);
1575 while (true) switch (token_tags[first_tok - 1]) {
1576 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1577 else => break,
1578 };
1579 return tree.tokensToSpan(
1580 first_tok,
1581 tree.lastToken(node),
1582 first_tok,
1583 );
1584 },
1585 .token_offset_param => |token_off| {
1586 const tree = try src_loc.file_scope.getTree(gpa);
1587 const token_tags = tree.tokens.items(.tag);
1588 const main_token = tree.nodes.items(.main_token)[src_loc.base_node];
1589 const tok_index = @as(Ast.TokenIndex, @bitCast(token_off + @as(i32, @bitCast(main_token))));
1590
1591 var first_tok = tok_index;
1592 while (true) switch (token_tags[first_tok - 1]) {
1593 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1594 else => break,
1595 };
1596 return tree.tokensToSpan(
1597 first_tok,
1598 tok_index,
1599 first_tok,
1600 );
1601 },
1602
1603 .node_offset_anyframe_type => |node_off| {
1604 const tree = try src_loc.file_scope.getTree(gpa);
1605 const node_datas = tree.nodes.items(.data);
1606 const parent_node = src_loc.relativeToNodeIndex(node_off);
1607 return tree.nodeToSpan(node_datas[parent_node].rhs);
1608 },
1609
1610 .node_offset_lib_name => |node_off| {
1611 const tree = try src_loc.file_scope.getTree(gpa);
1612 const parent_node = src_loc.relativeToNodeIndex(node_off);
1613 var buf: [1]Ast.Node.Index = undefined;
1614 const full = tree.fullFnProto(&buf, parent_node).?;
1615 const tok_index = full.lib_name.?;
1616 const start = tree.tokens.items(.start)[tok_index];
1617 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1618 return Span{ .start = start, .end = end, .main = start };
1619 },
1620
1621 .node_offset_array_type_len => |node_off| {
1622 const tree = try src_loc.file_scope.getTree(gpa);
1623 const parent_node = src_loc.relativeToNodeIndex(node_off);
1624
1625 const full = tree.fullArrayType(parent_node).?;
1626 return tree.nodeToSpan(full.ast.elem_count);
1627 },
1628 .node_offset_array_type_sentinel => |node_off| {
1629 const tree = try src_loc.file_scope.getTree(gpa);
1630 const parent_node = src_loc.relativeToNodeIndex(node_off);
1631
1632 const full = tree.fullArrayType(parent_node).?;
1633 return tree.nodeToSpan(full.ast.sentinel);
1634 },
1635 .node_offset_array_type_elem => |node_off| {
1636 const tree = try src_loc.file_scope.getTree(gpa);
1637 const parent_node = src_loc.relativeToNodeIndex(node_off);
1638
1639 const full = tree.fullArrayType(parent_node).?;
1640 return tree.nodeToSpan(full.ast.elem_type);
1641 },
1642 .node_offset_un_op => |node_off| {
1643 const tree = try src_loc.file_scope.getTree(gpa);
1644 const node_datas = tree.nodes.items(.data);
1645 const node = src_loc.relativeToNodeIndex(node_off);
1646
1647 return tree.nodeToSpan(node_datas[node].lhs);
1648 },
1649 .node_offset_ptr_elem => |node_off| {
1650 const tree = try src_loc.file_scope.getTree(gpa);
1651 const parent_node = src_loc.relativeToNodeIndex(node_off);
1652
1653 const full = tree.fullPtrType(parent_node).?;
1654 return tree.nodeToSpan(full.ast.child_type);
1655 },
1656 .node_offset_ptr_sentinel => |node_off| {
1657 const tree = try src_loc.file_scope.getTree(gpa);
1658 const parent_node = src_loc.relativeToNodeIndex(node_off);
1659
1660 const full = tree.fullPtrType(parent_node).?;
1661 return tree.nodeToSpan(full.ast.sentinel);
1662 },
1663 .node_offset_ptr_align => |node_off| {
1664 const tree = try src_loc.file_scope.getTree(gpa);
1665 const parent_node = src_loc.relativeToNodeIndex(node_off);
1666
1667 const full = tree.fullPtrType(parent_node).?;
1668 return tree.nodeToSpan(full.ast.align_node);
1669 },
1670 .node_offset_ptr_addrspace => |node_off| {
1671 const tree = try src_loc.file_scope.getTree(gpa);
1672 const parent_node = src_loc.relativeToNodeIndex(node_off);
1673
1674 const full = tree.fullPtrType(parent_node).?;
1675 return tree.nodeToSpan(full.ast.addrspace_node);
1676 },
1677 .node_offset_ptr_bitoffset => |node_off| {
1678 const tree = try src_loc.file_scope.getTree(gpa);
1679 const parent_node = src_loc.relativeToNodeIndex(node_off);
1680
1681 const full = tree.fullPtrType(parent_node).?;
1682 return tree.nodeToSpan(full.ast.bit_range_start);
1683 },
1684 .node_offset_ptr_hostsize => |node_off| {
1685 const tree = try src_loc.file_scope.getTree(gpa);
1686 const parent_node = src_loc.relativeToNodeIndex(node_off);
1687
1688 const full = tree.fullPtrType(parent_node).?;
1689 return tree.nodeToSpan(full.ast.bit_range_end);
1690 },
1691 .node_offset_container_tag => |node_off| {
1692 const tree = try src_loc.file_scope.getTree(gpa);
1693 const node_tags = tree.nodes.items(.tag);
1694 const parent_node = src_loc.relativeToNodeIndex(node_off);
1695
1696 switch (node_tags[parent_node]) {
1697 .container_decl_arg, .container_decl_arg_trailing => {
1698 const full = tree.containerDeclArg(parent_node);
1699 return tree.nodeToSpan(full.ast.arg);
1700 },
1701 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => {
1702 const full = tree.taggedUnionEnumTag(parent_node);
1703
1704 return tree.tokensToSpan(
1705 tree.firstToken(full.ast.arg) - 2,
1706 tree.lastToken(full.ast.arg) + 1,
1707 tree.nodes.items(.main_token)[full.ast.arg],
1708 );
1709 },
1710 else => unreachable,
1711 }
1712 },
1713 .node_offset_field_default => |node_off| {
1714 const tree = try src_loc.file_scope.getTree(gpa);
1715 const node_tags = tree.nodes.items(.tag);
1716 const parent_node = src_loc.relativeToNodeIndex(node_off);
1717
1718 const full: Ast.full.ContainerField = switch (node_tags[parent_node]) {
1719 .container_field => tree.containerField(parent_node),
1720 .container_field_init => tree.containerFieldInit(parent_node),
1721 else => unreachable,
1722 };
1723 return tree.nodeToSpan(full.ast.value_expr);
1724 },
1725 .node_offset_init_ty => |node_off| {
1726 const tree = try src_loc.file_scope.getTree(gpa);
1727 const parent_node = src_loc.relativeToNodeIndex(node_off);
1728
1729 var buf: [2]Ast.Node.Index = undefined;
1730 const type_expr = if (tree.fullArrayInit(&buf, parent_node)) |array_init|
1731 array_init.ast.type_expr
1732 else
1733 tree.fullStructInit(&buf, parent_node).?.ast.type_expr;
1734 return tree.nodeToSpan(type_expr);
1735 },
1736 .node_offset_store_ptr => |node_off| {
1737 const tree = try src_loc.file_scope.getTree(gpa);
1738 const node_tags = tree.nodes.items(.tag);
1739 const node_datas = tree.nodes.items(.data);
1740 const node = src_loc.relativeToNodeIndex(node_off);
1741
1742 switch (node_tags[node]) {
1743 .assign => {
1744 return tree.nodeToSpan(node_datas[node].lhs);
1745 },
1746 else => return tree.nodeToSpan(node),
1747 }
1748 },
1749 .node_offset_store_operand => |node_off| {
1750 const tree = try src_loc.file_scope.getTree(gpa);
1751 const node_tags = tree.nodes.items(.tag);
1752 const node_datas = tree.nodes.items(.data);
1753 const node = src_loc.relativeToNodeIndex(node_off);
1754
1755 switch (node_tags[node]) {
1756 .assign => {
1757 return tree.nodeToSpan(node_datas[node].rhs);
1758 },
1759 else => return tree.nodeToSpan(node),
1760 }
1761 },
1762 .node_offset_return_operand => |node_off| {
1763 const tree = try src_loc.file_scope.getTree(gpa);
1764 const node = src_loc.relativeToNodeIndex(node_off);
1765 const node_tags = tree.nodes.items(.tag);
1766 const node_datas = tree.nodes.items(.data);
1767 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) {
1768 return tree.nodeToSpan(node_datas[node].lhs);
1769 }
1770 return tree.nodeToSpan(node);
1771 },
1772 .container_field_name,
1773 .container_field_value,
1774 .container_field_type,
1775 .container_field_align,
1776 => |field_idx| {
1777 const tree = try src_loc.file_scope.getTree(gpa);
1778 const node = src_loc.relativeToNodeIndex(0);
1779 var buf: [2]Ast.Node.Index = undefined;
1780 const container_decl = tree.fullContainerDecl(&buf, node) orelse
1781 return tree.nodeToSpan(node);
1782
1783 var cur_field_idx: usize = 0;
1784 for (container_decl.ast.members) |member_node| {
1785 const field = tree.fullContainerField(member_node) orelse continue;
1786 if (cur_field_idx < field_idx) {
1787 cur_field_idx += 1;
1788 continue;
1789 }
1790 const field_component_node = switch (src_loc.lazy) {
1791 .container_field_name => 0,
1792 .container_field_value => field.ast.value_expr,
1793 .container_field_type => field.ast.type_expr,
1794 .container_field_align => field.ast.align_expr,
1795 else => unreachable,
1796 };
1797 if (field_component_node == 0) {
1798 return tree.tokenToSpan(field.ast.main_token);
1799 } else {
1800 return tree.nodeToSpan(field_component_node);
1801 }
1802 } else unreachable;
1803 },
1804 .init_elem => |init_elem| {
1805 const tree = try src_loc.file_scope.getTree(gpa);
1806 const init_node = src_loc.relativeToNodeIndex(init_elem.init_node_offset);
1807 var buf: [2]Ast.Node.Index = undefined;
1808 if (tree.fullArrayInit(&buf, init_node)) |full| {
1809 const elem_node = full.ast.elements[init_elem.elem_index];
1810 return tree.nodeToSpan(elem_node);
1811 } else if (tree.fullStructInit(&buf, init_node)) |full| {
1812 const field_node = full.ast.fields[init_elem.elem_index];
1813 return tree.tokensToSpan(
1814 tree.firstToken(field_node) - 3,
1815 tree.lastToken(field_node),
1816 tree.nodes.items(.main_token)[field_node] - 2,
1817 );
1818 } else unreachable;
1819 },
1820 .init_field_name,
1821 .init_field_linkage,
1822 .init_field_section,
1823 .init_field_visibility,
1824 .init_field_rw,
1825 .init_field_locality,
1826 .init_field_cache,
1827 .init_field_library,
1828 .init_field_thread_local,
1829 => |builtin_call_node| {
1830 const wanted = switch (src_loc.lazy) {
1831 .init_field_name => "name",
1832 .init_field_linkage => "linkage",
1833 .init_field_section => "section",
1834 .init_field_visibility => "visibility",
1835 .init_field_rw => "rw",
1836 .init_field_locality => "locality",
1837 .init_field_cache => "cache",
1838 .init_field_library => "library",
1839 .init_field_thread_local => "thread_local",
1840 else => unreachable,
1841 };
1842 const tree = try src_loc.file_scope.getTree(gpa);
1843 const node_datas = tree.nodes.items(.data);
1844 const node_tags = tree.nodes.items(.tag);
1845 const node = src_loc.relativeToNodeIndex(builtin_call_node);
1846 const arg_node = switch (node_tags[node]) {
1847 .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs,
1848 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1],
1849 else => unreachable,
1850 };
1851 var buf: [2]Ast.Node.Index = undefined;
1852 const full = tree.fullStructInit(&buf, arg_node) orelse
1853 return tree.nodeToSpan(arg_node);
1854 for (full.ast.fields) |field_node| {
1855 // . IDENTIFIER = field_node
1856 const name_token = tree.firstToken(field_node) - 2;
1857 const name = tree.tokenSlice(name_token);
1858 if (std.mem.eql(u8, name, wanted)) {
1859 return tree.tokensToSpan(
1860 name_token - 1,
1861 tree.lastToken(field_node),
1862 tree.nodes.items(.main_token)[field_node] - 2,
1863 );
1864 }
1865 }
1866 return tree.nodeToSpan(arg_node);
1867 },
1868 .switch_case_item,
1869 .switch_case_item_range_first,
1870 .switch_case_item_range_last,
1871 .switch_capture,
1872 .switch_tag_capture,
1873 => {
1874 const switch_node_offset, const want_case_idx = switch (src_loc.lazy) {
1875 .switch_case_item,
1876 .switch_case_item_range_first,
1877 .switch_case_item_range_last,
1878 => |x| .{ x.switch_node_offset, x.case_idx },
1879 .switch_capture,
1880 .switch_tag_capture,
1881 => |x| .{ x.switch_node_offset, x.case_idx },
1882 else => unreachable,
1883 };
1884
1885 const tree = try src_loc.file_scope.getTree(gpa);
1886 const node_datas = tree.nodes.items(.data);
1887 const node_tags = tree.nodes.items(.tag);
1888 const main_tokens = tree.nodes.items(.main_token);
1889 const switch_node = src_loc.relativeToNodeIndex(switch_node_offset);
1890 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
1891 const case_nodes = tree.extra_data[extra.start..extra.end];
1892
1893 var multi_i: u32 = 0;
1894 var scalar_i: u32 = 0;
1895 const case = for (case_nodes) |case_node| {
1896 const case = tree.fullSwitchCase(case_node).?;
1897 const is_special = special: {
1898 if (case.ast.values.len == 0) break :special true;
1899 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .identifier) {
1900 break :special mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_");
1901 }
1902 break :special false;
1903 };
1904 if (is_special) {
1905 if (want_case_idx.isSpecial()) {
1906 break case;
1907 }
1908 }
1909
1910 const is_multi = case.ast.values.len != 1 or
1911 node_tags[case.ast.values[0]] == .switch_range;
1912
1913 if (!want_case_idx.isSpecial()) switch (want_case_idx.kind) {
1914 .scalar => if (!is_multi and want_case_idx.index == scalar_i) break case,
1915 .multi => if (is_multi and want_case_idx.index == multi_i) break case,
1916 };
1917
1918 if (is_multi) {
1919 multi_i += 1;
1920 } else {
1921 scalar_i += 1;
1922 }
1923 } else unreachable;
1924
1925 const want_item = switch (src_loc.lazy) {
1926 .switch_case_item,
1927 .switch_case_item_range_first,
1928 .switch_case_item_range_last,
1929 => |x| x.item_idx,
1930 .switch_capture, .switch_tag_capture => {
1931 const token_tags = tree.tokens.items(.tag);
1932 const start = switch (src_loc.lazy) {
1933 .switch_capture => case.payload_token.?,
1934 .switch_tag_capture => tok: {
1935 var tok = case.payload_token.?;
1936 if (token_tags[tok] == .asterisk) tok += 1;
1937 tok += 2; // skip over comma
1938 break :tok tok;
1939 },
1940 else => unreachable,
1941 };
1942 const end = switch (token_tags[start]) {
1943 .asterisk => start + 1,
1944 else => start,
1945 };
1946 return tree.tokensToSpan(start, end, start);
1947 },
1948 else => unreachable,
1949 };
1950
1951 switch (want_item.kind) {
1952 .single => {
1953 var item_i: u32 = 0;
1954 for (case.ast.values) |item_node| {
1955 if (node_tags[item_node] == .switch_range) continue;
1956 if (item_i != want_item.index) {
1957 item_i += 1;
1958 continue;
1959 }
1960 return tree.nodeToSpan(item_node);
1961 } else unreachable;
1962 },
1963 .range => {
1964 var range_i: u32 = 0;
1965 for (case.ast.values) |item_node| {
1966 if (node_tags[item_node] != .switch_range) continue;
1967 if (range_i != want_item.index) {
1968 range_i += 1;
1969 continue;
1970 }
1971 return switch (src_loc.lazy) {
1972 .switch_case_item => tree.nodeToSpan(item_node),
1973 .switch_case_item_range_first => tree.nodeToSpan(node_datas[item_node].lhs),
1974 .switch_case_item_range_last => tree.nodeToSpan(node_datas[item_node].rhs),
1975 else => unreachable,
1976 };
1977 } else unreachable;
1978 },
1979 }
1980 },
1981 }
1982 }
1983};
1984
1985pub const LazySrcLoc = struct {
1986 /// This instruction provides the source node locations are resolved relative to.
1987 /// It is a `declaration`, `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl`.
1988 /// This must be valid even if `relative` is an absolute value, since it is required to
1989 /// determine the file which the `LazySrcLoc` refers to.
1990 base_node_inst: InternPool.TrackedInst.Index,
1991 /// This field determines the source location relative to `base_node_inst`.
1992 offset: Offset,
1993
1994 pub const Offset = union(enum) {
1995 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
1996 /// that all code paths which would need to resolve the source location are
1997 /// unreachable. If you are debugging this tag incorrectly being this value,
1998 /// look into using reverse-continue with a memory watchpoint to see where the
1999 /// value is being set to this tag.
2000 /// `base_node_inst` is unused.
2001 unneeded,
2002 /// Means the source location points to an entire file; not any particular
2003 /// location within the file. `file_scope` union field will be active.
2004 entire_file,
2005 /// The source location points to a byte offset within a source file,
2006 /// offset from 0. The source file is determined contextually.
2007 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
2008 byte_abs: u32,
2009 /// The source location points to a token within a source file,
2010 /// offset from 0. The source file is determined contextually.
2011 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
2012 token_abs: u32,
2013 /// The source location points to an AST node within a source file,
2014 /// offset from 0. The source file is determined contextually.
2015 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
2016 node_abs: u32,
2017 /// The source location points to a byte offset within a source file,
2018 /// offset from the byte offset of the base node within the file.
2019 byte_offset: u32,
2020 /// This data is the offset into the token list from the base node's first token.
2021 token_offset: u32,
2022 /// The source location points to an AST node, which is this value offset
2023 /// from its containing base node AST index.
2024 node_offset: TracedOffset,
2025 /// The source location points to the main token of an AST node, found
2026 /// by taking this AST node index offset from the containing base node.
2027 node_offset_main_token: i32,
2028 /// The source location points to the beginning of a struct initializer.
2029 node_offset_initializer: i32,
2030 /// The source location points to a variable declaration type expression,
2031 /// found by taking this AST node index offset from the containing
2032 /// base node, which points to a variable declaration AST node. Next, navigate
2033 /// to the type expression.
2034 node_offset_var_decl_ty: i32,
2035 /// The source location points to the alignment expression of a var decl.
2036 node_offset_var_decl_align: i32,
2037 /// The source location points to the linksection expression of a var decl.
2038 node_offset_var_decl_section: i32,
2039 /// The source location points to the addrspace expression of a var decl.
2040 node_offset_var_decl_addrspace: i32,
2041 /// The source location points to the initializer of a var decl.
2042 node_offset_var_decl_init: i32,
2043 /// The source location points to the given argument of a builtin function call.
2044 /// `builtin_call_node` points to the builtin call.
2045 /// `arg_index` is the index of the argument which hte source location refers to.
2046 node_offset_builtin_call_arg: struct {
2047 builtin_call_node: i32,
2048 arg_index: u32,
2049 },
2050 /// Like `node_offset_builtin_call_arg` but recurses through arbitrarily many calls
2051 /// to pointer cast builtins (taking the first argument of the most nested).
2052 node_offset_ptrcast_operand: i32,
2053 /// The source location points to the index expression of an array access
2054 /// expression, found by taking this AST node index offset from the containing
2055 /// base node, which points to an array access AST node. Next, navigate
2056 /// to the index expression.
2057 node_offset_array_access_index: i32,
2058 /// The source location points to the LHS of a slice expression
2059 /// expression, found by taking this AST node index offset from the containing
2060 /// base node, which points to a slice AST node. Next, navigate
2061 /// to the sentinel expression.
2062 node_offset_slice_ptr: i32,
2063 /// The source location points to start expression of a slice expression
2064 /// expression, found by taking this AST node index offset from the containing
2065 /// base node, which points to a slice AST node. Next, navigate
2066 /// to the sentinel expression.
2067 node_offset_slice_start: i32,
2068 /// The source location points to the end expression of a slice
2069 /// expression, found by taking this AST node index offset from the containing
2070 /// base node, which points to a slice AST node. Next, navigate
2071 /// to the sentinel expression.
2072 node_offset_slice_end: i32,
2073 /// The source location points to the sentinel expression of a slice
2074 /// expression, found by taking this AST node index offset from the containing
2075 /// base node, which points to a slice AST node. Next, navigate
2076 /// to the sentinel expression.
2077 node_offset_slice_sentinel: i32,
2078 /// The source location points to the callee expression of a function
2079 /// call expression, found by taking this AST node index offset from the containing
2080 /// base node, which points to a function call AST node. Next, navigate
2081 /// to the callee expression.
2082 node_offset_call_func: i32,
2083 /// The payload is offset from the containing base node.
2084 /// The source location points to the field name of:
2085 /// * a field access expression (`a.b`), or
2086 /// * the callee of a method call (`a.b()`)
2087 node_offset_field_name: i32,
2088 /// The payload is offset from the containing base node.
2089 /// The source location points to the field name of the operand ("b" node)
2090 /// of a field initialization expression (`.a = b`)
2091 node_offset_field_name_init: i32,
2092 /// The source location points to the pointer of a pointer deref expression,
2093 /// found by taking this AST node index offset from the containing
2094 /// base node, which points to a pointer deref AST node. Next, navigate
2095 /// to the pointer expression.
2096 node_offset_deref_ptr: i32,
2097 /// The source location points to the assembly source code of an inline assembly
2098 /// expression, found by taking this AST node index offset from the containing
2099 /// base node, which points to inline assembly AST node. Next, navigate
2100 /// to the asm template source code.
2101 node_offset_asm_source: i32,
2102 /// The source location points to the return type of an inline assembly
2103 /// expression, found by taking this AST node index offset from the containing
2104 /// base node, which points to inline assembly AST node. Next, navigate
2105 /// to the return type expression.
2106 node_offset_asm_ret_ty: i32,
2107 /// The source location points to the condition expression of an if
2108 /// expression, found by taking this AST node index offset from the containing
2109 /// base node, which points to an if expression AST node. Next, navigate
2110 /// to the condition expression.
2111 node_offset_if_cond: i32,
2112 /// The source location points to a binary expression, such as `a + b`, found
2113 /// by taking this AST node index offset from the containing base node.
2114 node_offset_bin_op: i32,
2115 /// The source location points to the LHS of a binary expression, found
2116 /// by taking this AST node index offset from the containing base node,
2117 /// which points to a binary expression AST node. Next, navigate to the LHS.
2118 node_offset_bin_lhs: i32,
2119 /// The source location points to the RHS of a binary expression, found
2120 /// by taking this AST node index offset from the containing base node,
2121 /// which points to a binary expression AST node. Next, navigate to the RHS.
2122 node_offset_bin_rhs: i32,
2123 /// The source location points to the operand of a switch expression, found
2124 /// by taking this AST node index offset from the containing base node,
2125 /// which points to a switch expression AST node. Next, navigate to the operand.
2126 node_offset_switch_operand: i32,
2127 /// The source location points to the else/`_` prong of a switch expression, found
2128 /// by taking this AST node index offset from the containing base node,
2129 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.
2130 node_offset_switch_special_prong: i32,
2131 /// The source location points to all the ranges of a switch expression, found
2132 /// by taking this AST node index offset from the containing base node,
2133 /// which points to a switch expression AST node. Next, navigate to any of the
2134 /// range nodes. The error applies to all of them.
2135 node_offset_switch_range: i32,
2136 /// The source location points to the align expr of a function type
2137 /// expression, found by taking this AST node index offset from the containing
2138 /// base node, which points to a function type AST node. Next, navigate to
2139 /// the calling convention node.
2140 node_offset_fn_type_align: i32,
2141 /// The source location points to the addrspace expr of a function type
2142 /// expression, found by taking this AST node index offset from the containing
2143 /// base node, which points to a function type AST node. Next, navigate to
2144 /// the calling convention node.
2145 node_offset_fn_type_addrspace: i32,
2146 /// The source location points to the linksection expr of a function type
2147 /// expression, found by taking this AST node index offset from the containing
2148 /// base node, which points to a function type AST node. Next, navigate to
2149 /// the calling convention node.
2150 node_offset_fn_type_section: i32,
2151 /// The source location points to the calling convention of a function type
2152 /// expression, found by taking this AST node index offset from the containing
2153 /// base node, which points to a function type AST node. Next, navigate to
2154 /// the calling convention node.
2155 node_offset_fn_type_cc: i32,
2156 /// The source location points to the return type of a function type
2157 /// expression, found by taking this AST node index offset from the containing
2158 /// base node, which points to a function type AST node. Next, navigate to
2159 /// the return type node.
2160 node_offset_fn_type_ret_ty: i32,
2161 node_offset_param: i32,
2162 token_offset_param: i32,
2163 /// The source location points to the type expression of an `anyframe->T`
2164 /// expression, found by taking this AST node index offset from the containing
2165 /// base node, which points to a `anyframe->T` expression AST node. Next, navigate
2166 /// to the type expression.
2167 node_offset_anyframe_type: i32,
2168 /// The source location points to the string literal of `extern "foo"`, found
2169 /// by taking this AST node index offset from the containing
2170 /// base node, which points to a function prototype or variable declaration
2171 /// expression AST node. Next, navigate to the string literal of the `extern "foo"`.
2172 node_offset_lib_name: i32,
2173 /// The source location points to the len expression of an `[N:S]T`
2174 /// expression, found by taking this AST node index offset from the containing
2175 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2176 /// to the len expression.
2177 node_offset_array_type_len: i32,
2178 /// The source location points to the sentinel expression of an `[N:S]T`
2179 /// expression, found by taking this AST node index offset from the containing
2180 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2181 /// to the sentinel expression.
2182 node_offset_array_type_sentinel: i32,
2183 /// The source location points to the elem expression of an `[N:S]T`
2184 /// expression, found by taking this AST node index offset from the containing
2185 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2186 /// to the elem expression.
2187 node_offset_array_type_elem: i32,
2188 /// The source location points to the operand of an unary expression.
2189 node_offset_un_op: i32,
2190 /// The source location points to the elem type of a pointer.
2191 node_offset_ptr_elem: i32,
2192 /// The source location points to the sentinel of a pointer.
2193 node_offset_ptr_sentinel: i32,
2194 /// The source location points to the align expr of a pointer.
2195 node_offset_ptr_align: i32,
2196 /// The source location points to the addrspace expr of a pointer.
2197 node_offset_ptr_addrspace: i32,
2198 /// The source location points to the bit-offset of a pointer.
2199 node_offset_ptr_bitoffset: i32,
2200 /// The source location points to the host size of a pointer.
2201 node_offset_ptr_hostsize: i32,
2202 /// The source location points to the tag type of an union or an enum.
2203 node_offset_container_tag: i32,
2204 /// The source location points to the default value of a field.
2205 node_offset_field_default: i32,
2206 /// The source location points to the type of an array or struct initializer.
2207 node_offset_init_ty: i32,
2208 /// The source location points to the LHS of an assignment.
2209 node_offset_store_ptr: i32,
2210 /// The source location points to the RHS of an assignment.
2211 node_offset_store_operand: i32,
2212 /// The source location points to the operand of a `return` statement, or
2213 /// the `return` itself if there is no explicit operand.
2214 node_offset_return_operand: i32,
2215 /// The source location points to a for loop input.
2216 for_input: struct {
2217 /// Points to the for loop AST node.
2218 for_node_offset: i32,
2219 /// Picks one of the inputs from the condition.
2220 input_index: u32,
2221 },
2222 /// The source location points to one of the captures of a for loop, found
2223 /// by taking this AST node index offset from the containing
2224 /// base node, which points to one of the input nodes of a for loop.
2225 /// Next, navigate to the corresponding capture.
2226 for_capture_from_input: i32,
2227 /// The source location points to the argument node of a function call.
2228 call_arg: struct {
2229 /// Points to the function call AST node.
2230 call_node_offset: i32,
2231 /// The index of the argument the source location points to.
2232 arg_index: u32,
2233 },
2234 fn_proto_param: FnProtoParam,
2235 fn_proto_param_type: FnProtoParam,
2236 array_cat_lhs: ArrayCat,
2237 array_cat_rhs: ArrayCat,
2238 /// The source location points to the name of the field at the given index
2239 /// of the container type declaration at the base node.
2240 container_field_name: u32,
2241 /// Like `continer_field_name`, but points at the field's default value.
2242 container_field_value: u32,
2243 /// Like `continer_field_name`, but points at the field's type.
2244 container_field_type: u32,
2245 /// Like `continer_field_name`, but points at the field's alignment.
2246 container_field_align: u32,
2247 /// The source location points to the given element/field of a struct or
2248 /// array initialization expression.
2249 init_elem: struct {
2250 /// Points to the AST node of the initialization expression.
2251 init_node_offset: i32,
2252 /// The index of the field/element the source location points to.
2253 elem_index: u32,
2254 },
2255 // The following source locations are like `init_elem`, but refer to a
2256 // field with a specific name. If such a field is not given, the entire
2257 // initialization expression is used instead.
2258 // The `i32` points to the AST node of a builtin call, whose *second*
2259 // argument is the init expression.
2260 init_field_name: i32,
2261 init_field_linkage: i32,
2262 init_field_section: i32,
2263 init_field_visibility: i32,
2264 init_field_rw: i32,
2265 init_field_locality: i32,
2266 init_field_cache: i32,
2267 init_field_library: i32,
2268 init_field_thread_local: i32,
2269 /// The source location points to the value of an item in a specific
2270 /// case of a `switch`.
2271 switch_case_item: SwitchItem,
2272 /// The source location points to the "first" value of a range item in
2273 /// a specific case of a `switch`.
2274 switch_case_item_range_first: SwitchItem,
2275 /// The source location points to the "last" value of a range item in
2276 /// a specific case of a `switch`.
2277 switch_case_item_range_last: SwitchItem,
2278 /// The source location points to the main capture of a specific case of
2279 /// a `switch`.
2280 switch_capture: SwitchCapture,
2281 /// The source location points to the "tag" capture (second capture) of
2282 /// a specific case of a `switch`.
2283 switch_tag_capture: SwitchCapture,
2284
2285 pub const FnProtoParam = struct {
2286 /// The offset of the function prototype AST node.
2287 fn_proto_node_offset: i32,
2288 /// The index of the parameter the source location points to.
2289 param_index: u32,
2290 };
2291
2292 pub const SwitchItem = struct {
2293 /// The offset of the switch AST node.
2294 switch_node_offset: i32,
2295 /// The index of the case to point to within this switch.
2296 case_idx: SwitchCaseIndex,
2297 /// The index of the item to point to within this case.
2298 item_idx: SwitchItemIndex,
2299 };
2300
2301 pub const SwitchCapture = struct {
2302 /// The offset of the switch AST node.
2303 switch_node_offset: i32,
2304 /// The index of the case whose capture to point to.
2305 case_idx: SwitchCaseIndex,
2306 };
2307
2308 pub const SwitchCaseIndex = packed struct(u32) {
2309 kind: enum(u1) { scalar, multi },
2310 index: u31,
2311
2312 pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
2313 pub fn isSpecial(idx: SwitchCaseIndex) bool {
2314 return @as(u32, @bitCast(idx)) == @as(u32, @bitCast(special));
2315 }
2316 };
2317
2318 pub const SwitchItemIndex = packed struct(u32) {
2319 kind: enum(u1) { single, range },
2320 index: u31,
2321 };
2322
2323 const ArrayCat = struct {
2324 /// Points to the array concat AST node.
2325 array_cat_offset: i32,
2326 /// The index of the element the source location points to.
2327 elem_index: u32,
2328 };
2329
2330 pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
2331
2332 noinline fn nodeOffsetDebug(node_offset: i32) Offset {
2333 var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } };
2334 result.node_offset.trace.addAddr(@returnAddress(), "init");
2335 return result;
2336 }
2337
2338 fn nodeOffsetRelease(node_offset: i32) Offset {
2339 return .{ .node_offset = .{ .x = node_offset } };
2340 }
2341
2342 /// This wraps a simple integer in debug builds so that later on we can find out
2343 /// where in semantic analysis the value got set.
2344 pub const TracedOffset = struct {
2345 x: i32,
2346 trace: std.debug.Trace = std.debug.Trace.init,
2347
2348 const want_tracing = false;
2349 };
2350 };
2351
2352 pub const unneeded: LazySrcLoc = .{
2353 .base_node_inst = undefined,
2354 .offset = .unneeded,
2355 };
2356
2357 pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) struct { *File, Ast.Node.Index } {
2358 const want_path_digest, const zir_inst = inst: {
2359 const info = base_node_inst.resolveFull(&zcu.intern_pool);
2360 break :inst .{ info.path_digest, info.inst };
2361 };
2362 const file = file: {
2363 const index = zcu.path_digest_map.getIndex(want_path_digest).?;
2364 break :file zcu.import_table.values()[index];
2365 };
2366 assert(file.zir_loaded);
2367
2368 const zir = file.zir;
2369 const inst = zir.instructions.get(@intFromEnum(zir_inst));
2370 const base_node: Ast.Node.Index = switch (inst.tag) {
2371 .declaration => inst.data.declaration.src_node,
2372 .extended => switch (inst.data.extended.opcode) {
2373 .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_node,
2374 .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_node,
2375 .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_node,
2376 .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_node,
2377 .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.node,
2378 else => unreachable,
2379 },
2380 else => unreachable,
2381 };
2382 return .{ file, base_node };
2383 }
2384
2385 /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`.
2386 /// TODO: it is incorrect to store a `SrcLoc` anywhere due to incremental compilation.
2387 /// Probably the type should be removed entirely and this resolution performed on-the-fly when needed.
2388 pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc {
2389 const file, const base_node = resolveBaseNode(lazy.base_node_inst, zcu);
2390 return .{
2391 .file_scope = file,
2392 .base_node = base_node,
2393 .lazy = lazy.offset,
2394 };
2395 }
2396};
2397
2398pub const SemaError = error{ OutOfMemory, AnalysisFail };
2399pub const CompileError = error{
2400 OutOfMemory,
2401 /// When this is returned, the compile error for the failure has already been recorded.
2402 AnalysisFail,
2403 /// A Type or Value was needed to be used during semantic analysis, but it was not available
2404 /// because the function is generic. This is only seen when analyzing the body of a param
2405 /// instruction.
2406 GenericPoison,
2407 /// In a comptime scope, a return instruction was encountered. This error is only seen when
2408 /// doing a comptime function call.
2409 ComptimeReturn,
2410 /// In a comptime scope, a break instruction was encountered. This error is only seen when
2411 /// evaluating a comptime block.
2412 ComptimeBreak,
2413};
2414
2415pub fn init(mod: *Module) !void {
2416 const gpa = mod.gpa;
2417 try mod.intern_pool.init(gpa);
2418 try mod.global_error_set.put(gpa, .empty, {});
2419}
2420
2421pub fn deinit(zcu: *Zcu) void {
2422 const gpa = zcu.gpa;
2423
2424 if (zcu.llvm_object) |llvm_object| {
2425 if (build_options.only_c) unreachable;
2426 llvm_object.deinit();
2427 }
2428
2429 for (zcu.import_table.keys()) |key| {
2430 gpa.free(key);
2431 }
2432 var failed_decls = zcu.failed_decls;
2433 zcu.failed_decls = .{};
2434 for (zcu.import_table.values()) |value| {
2435 value.destroy(zcu);
2436 }
2437 zcu.import_table.deinit(gpa);
2438 zcu.path_digest_map.deinit(gpa);
2439
2440 for (zcu.embed_table.keys(), zcu.embed_table.values()) |path, embed_file| {
2441 gpa.free(path);
2442 gpa.destroy(embed_file);
2443 }
2444 zcu.embed_table.deinit(gpa);
2445
2446 zcu.compile_log_text.deinit(gpa);
2447
2448 zcu.local_zir_cache.handle.close();
2449 zcu.global_zir_cache.handle.close();
2450
2451 for (failed_decls.values()) |value| {
2452 value.destroy(gpa);
2453 }
2454 failed_decls.deinit(gpa);
2455
2456 if (zcu.emit_h) |emit_h| {
2457 for (emit_h.failed_decls.values()) |value| {
2458 value.destroy(gpa);
2459 }
2460 emit_h.failed_decls.deinit(gpa);
2461 emit_h.decl_table.deinit(gpa);
2462 emit_h.allocated_emit_h.deinit(gpa);
2463 }
2464
2465 for (zcu.failed_files.values()) |value| {
2466 if (value) |msg| msg.destroy(gpa);
2467 }
2468 zcu.failed_files.deinit(gpa);
2469
2470 for (zcu.failed_embed_files.values()) |msg| {
2471 msg.destroy(gpa);
2472 }
2473 zcu.failed_embed_files.deinit(gpa);
2474
2475 for (zcu.failed_exports.values()) |value| {
2476 value.destroy(gpa);
2477 }
2478 zcu.failed_exports.deinit(gpa);
2479
2480 for (zcu.cimport_errors.values()) |*errs| {
2481 errs.deinit(gpa);
2482 }
2483 zcu.cimport_errors.deinit(gpa);
2484
2485 zcu.compile_log_decls.deinit(gpa);
2486
2487 for (zcu.decl_exports.values()) |*export_list| {
2488 export_list.deinit(gpa);
2489 }
2490 zcu.decl_exports.deinit(gpa);
2491
2492 for (zcu.value_exports.values()) |*export_list| {
2493 export_list.deinit(gpa);
2494 }
2495 zcu.value_exports.deinit(gpa);
2496
2497 for (zcu.export_owners.values()) |*value| {
2498 freeExportList(gpa, value);
2499 }
2500 zcu.export_owners.deinit(gpa);
2501
2502 zcu.global_error_set.deinit(gpa);
2503
2504 zcu.potentially_outdated.deinit(gpa);
2505 zcu.outdated.deinit(gpa);
2506 zcu.outdated_ready.deinit(gpa);
2507 zcu.outdated_file_root.deinit(gpa);
2508 zcu.retryable_failures.deinit(gpa);
2509
2510 zcu.test_functions.deinit(gpa);
2511
2512 for (zcu.global_assembly.values()) |s| {
2513 gpa.free(s);
2514 }
2515 zcu.global_assembly.deinit(gpa);
2516
2517 zcu.reference_table.deinit(gpa);
2518
2519 {
2520 var it = zcu.intern_pool.allocated_namespaces.iterator(0);
2521 while (it.next()) |namespace| {
2522 namespace.decls.deinit(gpa);
2523 namespace.usingnamespace_set.deinit(gpa);
2524 }
2525 }
2526
2527 zcu.intern_pool.deinit(gpa);
2528}
2529
2530pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
2531 const gpa = mod.gpa;
2532 const ip = &mod.intern_pool;
2533
2534 {
2535 _ = mod.test_functions.swapRemove(decl_index);
2536 if (mod.global_assembly.fetchSwapRemove(decl_index)) |kv| {
2537 gpa.free(kv.value);
2538 }
2539 }
2540
2541 ip.destroyDecl(gpa, decl_index);
2542
2543 if (mod.emit_h) |mod_emit_h| {
2544 const decl_emit_h = mod_emit_h.declPtr(decl_index);
2545 decl_emit_h.fwd_decl.deinit(gpa);
2546 decl_emit_h.* = undefined;
2547 }
2548}
2549
2550pub fn declPtr(mod: *Module, index: Decl.Index) *Decl {
2551 return mod.intern_pool.declPtr(index);
2552}
2553
2554pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace {
2555 return mod.intern_pool.namespacePtr(index);
2556}
2557
2558pub fn namespacePtrUnwrap(mod: *Module, index: Namespace.OptionalIndex) ?*Namespace {
2559 return mod.namespacePtr(index.unwrap() orelse return null);
2560}
2561
2562/// Returns true if and only if the Decl is the top level struct associated with a File.
2563pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
2564 const decl = mod.declPtr(decl_index);
2565 const namespace = mod.namespacePtr(decl.src_namespace);
2566 if (namespace.parent != .none) return false;
2567 return decl_index == namespace.decl_index;
2568}
2569
2570fn freeExportList(gpa: Allocator, export_list: *ArrayListUnmanaged(*Export)) void {
2571 for (export_list.items) |exp| gpa.destroy(exp);
2572 export_list.deinit(gpa);
2573}
2574
2575// TODO https://github.com/ziglang/zig/issues/8643
2576const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
2577const HackDataLayout = extern struct {
2578 data: [8]u8 align(@alignOf(Zir.Inst.Data)),
2579 safety_tag: u8,
2580};
2581comptime {
2582 if (data_has_safety_tag) {
2583 assert(@sizeOf(HackDataLayout) == @sizeOf(Zir.Inst.Data));
2584 }
2585}
2586
2587pub fn astGenFile(mod: *Module, file: *File) !void {
2588 assert(!file.mod.isBuiltin());
2589
2590 const tracy = trace(@src());
2591 defer tracy.end();
2592
2593 const comp = mod.comp;
2594 const gpa = mod.gpa;
2595
2596 // In any case we need to examine the stat of the file to determine the course of action.
2597 var source_file = try file.mod.root.openFile(file.sub_file_path, .{});
2598 defer source_file.close();
2599
2600 const stat = try source_file.stat();
2601
2602 const want_local_cache = file.mod == mod.main_mod;
2603 const hex_digest = hex: {
2604 var hex: Cache.HexDigest = undefined;
2605 _ = std.fmt.bufPrint(
2606 &hex,
2607 "{s}",
2608 .{std.fmt.fmtSliceHexLower(&file.path_digest)},
2609 ) catch unreachable;
2610 break :hex hex;
2611 };
2612 const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache;
2613 const zir_dir = cache_directory.handle;
2614
2615 // Determine whether we need to reload the file from disk and redo parsing and AstGen.
2616 var lock: std.fs.File.Lock = switch (file.status) {
2617 .never_loaded, .retryable_failure => lock: {
2618 // First, load the cached ZIR code, if any.
2619 log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{
2620 file.sub_file_path, want_local_cache, &hex_digest,
2621 });
2622
2623 break :lock .shared;
2624 },
2625 .parse_failure, .astgen_failure, .success_zir => lock: {
2626 const unchanged_metadata =
2627 stat.size == file.stat.size and
2628 stat.mtime == file.stat.mtime and
2629 stat.inode == file.stat.inode;
2630
2631 if (unchanged_metadata) {
2632 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
2633 return;
2634 }
2635
2636 log.debug("metadata changed: {s}", .{file.sub_file_path});
2637
2638 break :lock .exclusive;
2639 },
2640 };
2641
2642 // We ask for a lock in order to coordinate with other zig processes.
2643 // If another process is already working on this file, we will get the cached
2644 // version. Likewise if we're working on AstGen and another process asks for
2645 // the cached file, they'll get it.
2646 const cache_file = while (true) {
2647 break zir_dir.createFile(&hex_digest, .{
2648 .read = true,
2649 .truncate = false,
2650 .lock = lock,
2651 }) catch |err| switch (err) {
2652 error.NotDir => unreachable, // no dir components
2653 error.InvalidUtf8 => unreachable, // it's a hex encoded name
2654 error.InvalidWtf8 => unreachable, // it's a hex encoded name
2655 error.BadPathName => unreachable, // it's a hex encoded name
2656 error.NameTooLong => unreachable, // it's a fixed size name
2657 error.PipeBusy => unreachable, // it's not a pipe
2658 error.WouldBlock => unreachable, // not asking for non-blocking I/O
2659 // There are no dir components, so you would think that this was
2660 // unreachable, however we have observed on macOS two processes racing
2661 // to do openat() with O_CREAT manifest in ENOENT.
2662 error.FileNotFound => continue,
2663
2664 else => |e| return e, // Retryable errors are handled at callsite.
2665 };
2666 };
2667 defer cache_file.close();
2668
2669 while (true) {
2670 update: {
2671 // First we read the header to determine the lengths of arrays.
2672 const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) {
2673 // This can happen if Zig bails out of this function between creating
2674 // the cached file and writing it.
2675 error.EndOfStream => break :update,
2676 else => |e| return e,
2677 };
2678 const unchanged_metadata =
2679 stat.size == header.stat_size and
2680 stat.mtime == header.stat_mtime and
2681 stat.inode == header.stat_inode;
2682
2683 if (!unchanged_metadata) {
2684 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
2685 break :update;
2686 }
2687 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
2688 file.sub_file_path, header.instructions_len,
2689 });
2690
2691 file.zir = loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
2692 error.UnexpectedFileSize => {
2693 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
2694 break :update;
2695 },
2696 else => |e| return e,
2697 };
2698 file.zir_loaded = true;
2699 file.stat = .{
2700 .size = header.stat_size,
2701 .inode = header.stat_inode,
2702 .mtime = header.stat_mtime,
2703 };
2704 file.status = .success_zir;
2705 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
2706
2707 // TODO don't report compile errors until Sema @importFile
2708 if (file.zir.hasCompileErrors()) {
2709 {
2710 comp.mutex.lock();
2711 defer comp.mutex.unlock();
2712 try mod.failed_files.putNoClobber(gpa, file, null);
2713 }
2714 file.status = .astgen_failure;
2715 return error.AnalysisFail;
2716 }
2717 return;
2718 }
2719
2720 // If we already have the exclusive lock then it is our job to update.
2721 if (builtin.os.tag == .wasi or lock == .exclusive) break;
2722 // Otherwise, unlock to give someone a chance to get the exclusive lock
2723 // and then upgrade to an exclusive lock.
2724 cache_file.unlock();
2725 lock = .exclusive;
2726 try cache_file.lock(lock);
2727 }
2728
2729 // The cache is definitely stale so delete the contents to avoid an underwrite later.
2730 cache_file.setEndPos(0) catch |err| switch (err) {
2731 error.FileTooBig => unreachable, // 0 is not too big
2732
2733 else => |e| return e,
2734 };
2735
2736 mod.lockAndClearFileCompileError(file);
2737
2738 // If the previous ZIR does not have compile errors, keep it around
2739 // in case parsing or new ZIR fails. In case of successful ZIR update
2740 // at the end of this function we will free it.
2741 // We keep the previous ZIR loaded so that we can use it
2742 // for the update next time it does not have any compile errors. This avoids
2743 // needlessly tossing out semantic analysis work when an error is
2744 // temporarily introduced.
2745 if (file.zir_loaded and !file.zir.hasCompileErrors()) {
2746 assert(file.prev_zir == null);
2747 const prev_zir_ptr = try gpa.create(Zir);
2748 file.prev_zir = prev_zir_ptr;
2749 prev_zir_ptr.* = file.zir;
2750 file.zir = undefined;
2751 file.zir_loaded = false;
2752 }
2753 file.unload(gpa);
2754
2755 if (stat.size > std.math.maxInt(u32))
2756 return error.FileTooBig;
2757
2758 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
2759 defer if (!file.source_loaded) gpa.free(source);
2760 const amt = try source_file.readAll(source);
2761 if (amt != stat.size)
2762 return error.UnexpectedEndOfFile;
2763
2764 file.stat = .{
2765 .size = stat.size,
2766 .inode = stat.inode,
2767 .mtime = stat.mtime,
2768 };
2769 file.source = source;
2770 file.source_loaded = true;
2771
2772 file.tree = try Ast.parse(gpa, source, .zig);
2773 file.tree_loaded = true;
2774
2775 // Any potential AST errors are converted to ZIR errors here.
2776 file.zir = try AstGen.generate(gpa, file.tree);
2777 file.zir_loaded = true;
2778 file.status = .success_zir;
2779 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
2780
2781 const safety_buffer = if (data_has_safety_tag)
2782 try gpa.alloc([8]u8, file.zir.instructions.len)
2783 else
2784 undefined;
2785 defer if (data_has_safety_tag) gpa.free(safety_buffer);
2786 const data_ptr = if (data_has_safety_tag)
2787 if (file.zir.instructions.len == 0)
2788 @as([*]const u8, undefined)
2789 else
2790 @as([*]const u8, @ptrCast(safety_buffer.ptr))
2791 else
2792 @as([*]const u8, @ptrCast(file.zir.instructions.items(.data).ptr));
2793 if (data_has_safety_tag) {
2794 // The `Data` union has a safety tag but in the file format we store it without.
2795 for (file.zir.instructions.items(.data), 0..) |*data, i| {
2796 const as_struct = @as(*const HackDataLayout, @ptrCast(data));
2797 safety_buffer[i] = as_struct.data;
2798 }
2799 }
2800
2801 const header: Zir.Header = .{
2802 .instructions_len = @as(u32, @intCast(file.zir.instructions.len)),
2803 .string_bytes_len = @as(u32, @intCast(file.zir.string_bytes.len)),
2804 .extra_len = @as(u32, @intCast(file.zir.extra.len)),
2805
2806 .stat_size = stat.size,
2807 .stat_inode = stat.inode,
2808 .stat_mtime = stat.mtime,
2809 };
2810 var iovecs = [_]std.posix.iovec_const{
2811 .{
2812 .base = @as([*]const u8, @ptrCast(&header)),
2813 .len = @sizeOf(Zir.Header),
2814 },
2815 .{
2816 .base = @as([*]const u8, @ptrCast(file.zir.instructions.items(.tag).ptr)),
2817 .len = file.zir.instructions.len,
2818 },
2819 .{
2820 .base = data_ptr,
2821 .len = file.zir.instructions.len * 8,
2822 },
2823 .{
2824 .base = file.zir.string_bytes.ptr,
2825 .len = file.zir.string_bytes.len,
2826 },
2827 .{
2828 .base = @as([*]const u8, @ptrCast(file.zir.extra.ptr)),
2829 .len = file.zir.extra.len * 4,
2830 },
2831 };
2832 cache_file.writevAll(&iovecs) catch |err| {
2833 log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
2834 file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
2835 });
2836 };
2837
2838 if (file.zir.hasCompileErrors()) {
2839 {
2840 comp.mutex.lock();
2841 defer comp.mutex.unlock();
2842 try mod.failed_files.putNoClobber(gpa, file, null);
2843 }
2844 file.status = .astgen_failure;
2845 return error.AnalysisFail;
2846 }
2847
2848 if (file.prev_zir) |prev_zir| {
2849 try updateZirRefs(mod, file, prev_zir.*);
2850 // No need to keep previous ZIR.
2851 prev_zir.deinit(gpa);
2852 gpa.destroy(prev_zir);
2853 file.prev_zir = null;
2854 }
2855
2856 if (file.root_decl.unwrap()) |root_decl| {
2857 // The root of this file must be re-analyzed, since the file has changed.
2858 comp.mutex.lock();
2859 defer comp.mutex.unlock();
2860
2861 log.debug("outdated root Decl: {}", .{root_decl});
2862 try mod.outdated_file_root.put(gpa, root_decl, {});
2863 }
2864}
2865
2866pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
2867 return loadZirCacheBody(gpa, try cache_file.reader().readStruct(Zir.Header), cache_file);
2868}
2869
2870fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File) !Zir {
2871 var instructions: std.MultiArrayList(Zir.Inst) = .{};
2872 errdefer instructions.deinit(gpa);
2873
2874 try instructions.setCapacity(gpa, header.instructions_len);
2875 instructions.len = header.instructions_len;
2876
2877 var zir: Zir = .{
2878 .instructions = instructions.toOwnedSlice(),
2879 .string_bytes = &.{},
2880 .extra = &.{},
2881 };
2882 errdefer zir.deinit(gpa);
2883
2884 zir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
2885 zir.extra = try gpa.alloc(u32, header.extra_len);
2886
2887 const safety_buffer = if (data_has_safety_tag)
2888 try gpa.alloc([8]u8, header.instructions_len)
2889 else
2890 undefined;
2891 defer if (data_has_safety_tag) gpa.free(safety_buffer);
2892
2893 const data_ptr = if (data_has_safety_tag)
2894 @as([*]u8, @ptrCast(safety_buffer.ptr))
2895 else
2896 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr));
2897
2898 var iovecs = [_]std.posix.iovec{
2899 .{
2900 .base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)),
2901 .len = header.instructions_len,
2902 },
2903 .{
2904 .base = data_ptr,
2905 .len = header.instructions_len * 8,
2906 },
2907 .{
2908 .base = zir.string_bytes.ptr,
2909 .len = header.string_bytes_len,
2910 },
2911 .{
2912 .base = @as([*]u8, @ptrCast(zir.extra.ptr)),
2913 .len = header.extra_len * 4,
2914 },
2915 };
2916 const amt_read = try cache_file.readvAll(&iovecs);
2917 const amt_expected = zir.instructions.len * 9 +
2918 zir.string_bytes.len +
2919 zir.extra.len * 4;
2920 if (amt_read != amt_expected) return error.UnexpectedFileSize;
2921 if (data_has_safety_tag) {
2922 const tags = zir.instructions.items(.tag);
2923 for (zir.instructions.items(.data), 0..) |*data, i| {
2924 const union_tag = Zir.Inst.Tag.data_tags[@intFromEnum(tags[i])];
2925 const as_struct = @as(*HackDataLayout, @ptrCast(data));
2926 as_struct.* = .{
2927 .safety_tag = @intFromEnum(union_tag),
2928 .data = safety_buffer[i],
2929 };
2930 }
2931 }
2932
2933 return zir;
2934}
2935
2936/// This is called from the AstGen thread pool, so must acquire
2937/// the Compilation mutex when acting on shared state.
2938fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void {
2939 const gpa = zcu.gpa;
2940 const new_zir = file.zir;
2941
2942 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
2943 defer inst_map.deinit(gpa);
2944
2945 try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map);
2946
2947 const old_tag = old_zir.instructions.items(.tag);
2948 const old_data = old_zir.instructions.items(.data);
2949
2950 // TODO: this should be done after all AstGen workers complete, to avoid
2951 // iterating over this full set for every updated file.
2952 for (zcu.intern_pool.tracked_insts.keys(), 0..) |*ti, idx_raw| {
2953 const ti_idx: InternPool.TrackedInst.Index = @enumFromInt(idx_raw);
2954 if (!std.mem.eql(u8, &ti.path_digest, &file.path_digest)) continue;
2955 const old_inst = ti.inst;
2956 ti.inst = inst_map.get(ti.inst) orelse {
2957 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
2958 zcu.comp.mutex.lock();
2959 defer zcu.comp.mutex.unlock();
2960 log.debug("tracking failed for %{d}", .{old_inst});
2961 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx });
2962 continue;
2963 };
2964
2965 if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
2966 if (new_zir.getAssociatedSrcHash(ti.inst)) |new_hash| {
2967 if (std.zig.srcHashEql(old_hash, new_hash)) {
2968 break :hash_changed;
2969 }
2970 log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{
2971 old_inst,
2972 ti.inst,
2973 std.fmt.fmtSliceHexLower(&old_hash),
2974 std.fmt.fmtSliceHexLower(&new_hash),
2975 });
2976 }
2977 // The source hash associated with this instruction changed - invalidate relevant dependencies.
2978 zcu.comp.mutex.lock();
2979 defer zcu.comp.mutex.unlock();
2980 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx });
2981 }
2982
2983 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
2984 const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
2985 .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
2986 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
2987 else => false,
2988 },
2989 else => false,
2990 };
2991 if (!has_namespace) continue;
2992
2993 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
2994 defer old_names.deinit(zcu.gpa);
2995 {
2996 var it = old_zir.declIterator(old_inst);
2997 while (it.next()) |decl_inst| {
2998 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
2999 switch (decl_name) {
3000 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
3001 _ => if (decl_name.isNamedTest(old_zir)) continue,
3002 }
3003 const name_zir = decl_name.toString(old_zir).?;
3004 const name_ip = try zcu.intern_pool.getOrPutString(
3005 zcu.gpa,
3006 old_zir.nullTerminatedString(name_zir),
3007 .no_embedded_nulls,
3008 );
3009 try old_names.put(zcu.gpa, name_ip, {});
3010 }
3011 }
3012 var any_change = false;
3013 {
3014 var it = new_zir.declIterator(ti.inst);
3015 while (it.next()) |decl_inst| {
3016 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
3017 switch (decl_name) {
3018 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
3019 _ => if (decl_name.isNamedTest(old_zir)) continue,
3020 }
3021 const name_zir = decl_name.toString(old_zir).?;
3022 const name_ip = try zcu.intern_pool.getOrPutString(
3023 zcu.gpa,
3024 old_zir.nullTerminatedString(name_zir),
3025 .no_embedded_nulls,
3026 );
3027 if (!old_names.swapRemove(name_ip)) continue;
3028 // Name added
3029 any_change = true;
3030 zcu.comp.mutex.lock();
3031 defer zcu.comp.mutex.unlock();
3032 try zcu.markDependeeOutdated(.{ .namespace_name = .{
3033 .namespace = ti_idx,
3034 .name = name_ip,
3035 } });
3036 }
3037 }
3038 // The only elements remaining in `old_names` now are any names which were removed.
3039 for (old_names.keys()) |name_ip| {
3040 any_change = true;
3041 zcu.comp.mutex.lock();
3042 defer zcu.comp.mutex.unlock();
3043 try zcu.markDependeeOutdated(.{ .namespace_name = .{
3044 .namespace = ti_idx,
3045 .name = name_ip,
3046 } });
3047 }
3048
3049 if (any_change) {
3050 zcu.comp.mutex.lock();
3051 defer zcu.comp.mutex.unlock();
3052 try zcu.markDependeeOutdated(.{ .namespace = ti_idx });
3053 }
3054 }
3055}
3056
3057pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3058 log.debug("outdated dependee: {}", .{dependee});
3059 var it = zcu.intern_pool.dependencyIterator(dependee);
3060 while (it.next()) |depender| {
3061 if (zcu.outdated.contains(depender)) {
3062 // We do not need to increment the PO dep count, as if the outdated
3063 // dependee is a Decl, we had already marked this as PO.
3064 continue;
3065 }
3066 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);
3067 try zcu.outdated.putNoClobber(
3068 zcu.gpa,
3069 depender,
3070 // We do not need to increment this count for the same reason as above.
3071 if (opt_po_entry) |e| e.value else 0,
3072 );
3073 log.debug("outdated: {}", .{depender});
3074 if (opt_po_entry == null) {
3075 // This is a new entry with no PO dependencies.
3076 try zcu.outdated_ready.put(zcu.gpa, depender, {});
3077 }
3078 // If this is a Decl and was not previously PO, we must recursively
3079 // mark dependencies on its tyval as PO.
3080 if (opt_po_entry == null) {
3081 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
3082 }
3083 }
3084}
3085
3086fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3087 var it = zcu.intern_pool.dependencyIterator(dependee);
3088 while (it.next()) |depender| {
3089 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
3090 // This depender is already outdated, but it now has one
3091 // less PO dependency!
3092 po_dep_count.* -= 1;
3093 if (po_dep_count.* == 0) {
3094 try zcu.outdated_ready.put(zcu.gpa, depender, {});
3095 }
3096 continue;
3097 }
3098 // This depender is definitely at least PO, because this Decl was just analyzed
3099 // due to being outdated.
3100 const ptr = zcu.potentially_outdated.getPtr(depender).?;
3101 if (ptr.* > 1) {
3102 ptr.* -= 1;
3103 continue;
3104 }
3105
3106 // This dependency is no longer PO, i.e. is known to be up-to-date.
3107 assert(zcu.potentially_outdated.swapRemove(depender));
3108 // If this is a Decl, we must recursively mark dependencies on its tyval
3109 // as no longer PO.
3110 switch (depender.unwrap()) {
3111 .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }),
3112 .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }),
3113 }
3114 }
3115}
3116
3117/// Given a Depender which is newly outdated or PO, mark all Dependers which may
3118/// in turn be PO, due to a dependency on the original Depender's tyval or IES.
3119fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: InternPool.Depender) !void {
3120 var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) {
3121 .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced
3122 .func => |func_index| .{ .func_ies = func_index },
3123 });
3124
3125 while (it.next()) |po| {
3126 if (zcu.outdated.getPtr(po)) |po_dep_count| {
3127 // This dependency is already outdated, but it now has one more PO
3128 // dependency.
3129 if (po_dep_count.* == 0) {
3130 _ = zcu.outdated_ready.swapRemove(po);
3131 }
3132 po_dep_count.* += 1;
3133 continue;
3134 }
3135 if (zcu.potentially_outdated.getPtr(po)) |n| {
3136 // There is now one more PO dependency.
3137 n.* += 1;
3138 continue;
3139 }
3140 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
3141 // This Depender was not already PO, so we must recursively mark its dependers as also PO.
3142 try zcu.markTransitiveDependersPotentiallyOutdated(po);
3143 }
3144}
3145
3146pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender {
3147 if (!zcu.comp.debug_incremental) return null;
3148
3149 if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) {
3150 log.debug("findOutdatedToAnalyze: no outdated depender", .{});
3151 return null;
3152 }
3153
3154 // Our goal is to find an outdated Depender which itself has no outdated or
3155 // PO dependencies. Most of the time, such a Depender will exist - we track
3156 // them in the `outdated_ready` set for efficiency. However, this is not
3157 // necessarily the case, since the Decl dependency graph may contain loops
3158 // via mutually recursive definitions:
3159 // pub const A = struct { b: *B };
3160 // pub const B = struct { b: *A };
3161 // In this case, we must defer to more complex logic below.
3162
3163 if (zcu.outdated_ready.count() > 0) {
3164 log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{
3165 @tagName(zcu.outdated_ready.keys()[0].unwrap()),
3166 switch (zcu.outdated_ready.keys()[0].unwrap()) {
3167 inline else => |x| @intFromEnum(x),
3168 },
3169 });
3170 return zcu.outdated_ready.keys()[0];
3171 }
3172
3173 // Next, we will see if there is any outdated file root which was not in
3174 // `outdated`. This set will be small (number of files changed in this
3175 // update), so it's alright for us to just iterate here.
3176 for (zcu.outdated_file_root.keys()) |file_decl| {
3177 const decl_depender = InternPool.Depender.wrap(.{ .decl = file_decl });
3178 if (zcu.outdated.contains(decl_depender)) {
3179 // Since we didn't hit this in the first loop, this Decl must have
3180 // pending dependencies, so is ineligible.
3181 continue;
3182 }
3183 if (zcu.potentially_outdated.contains(decl_depender)) {
3184 // This Decl's struct may or may not need to be recreated depending
3185 // on whether it is outdated. If we analyzed it now, we would have
3186 // to assume it was outdated and recreate it!
3187 continue;
3188 }
3189 log.debug("findOutdatedToAnalyze: outdated file root decl '{d}'", .{file_decl});
3190 return decl_depender;
3191 }
3192
3193 // There is no single Depender which is ready for re-analysis. Instead, we
3194 // must assume that some Decl with PO dependencies is outdated - e.g. in the
3195 // above example we arbitrarily pick one of A or B. We should select a Decl,
3196 // since a Decl is definitely responsible for the loop in the dependency
3197 // graph (since you can't depend on a runtime function analysis!).
3198
3199 // The choice of this Decl could have a big impact on how much total
3200 // analysis we perform, since if analysis concludes its tyval is unchanged,
3201 // then other PO Dependers may be resolved as up-to-date. To hopefully avoid
3202 // doing too much work, let's find a Decl which the most things depend on -
3203 // the idea is that this will resolve a lot of loops (but this is only a
3204 // heuristic).
3205
3206 log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{
3207 zcu.outdated.count(),
3208 zcu.potentially_outdated.count(),
3209 });
3210
3211 var chosen_decl_idx: ?Decl.Index = null;
3212 var chosen_decl_dependers: u32 = undefined;
3213
3214 for (zcu.outdated.keys()) |depender| {
3215 const decl_index = switch (depender.unwrap()) {
3216 .decl => |d| d,
3217 .func => continue,
3218 };
3219
3220 var n: u32 = 0;
3221 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });
3222 while (it.next()) |_| n += 1;
3223
3224 if (chosen_decl_idx == null or n > chosen_decl_dependers) {
3225 chosen_decl_idx = decl_index;
3226 chosen_decl_dependers = n;
3227 }
3228 }
3229
3230 for (zcu.potentially_outdated.keys()) |depender| {
3231 const decl_index = switch (depender.unwrap()) {
3232 .decl => |d| d,
3233 .func => continue,
3234 };
3235
3236 var n: u32 = 0;
3237 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });
3238 while (it.next()) |_| n += 1;
3239
3240 if (chosen_decl_idx == null or n > chosen_decl_dependers) {
3241 chosen_decl_idx = decl_index;
3242 chosen_decl_dependers = n;
3243 }
3244 }
3245
3246 log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{
3247 chosen_decl_idx.?,
3248 chosen_decl_dependers,
3249 });
3250
3251 return InternPool.Depender.wrap(.{ .decl = chosen_decl_idx.? });
3252}
3253
3254/// During an incremental update, before semantic analysis, call this to flush all values from
3255/// `retryable_failures` and mark them as outdated so they get re-analyzed.
3256pub fn flushRetryableFailures(zcu: *Zcu) !void {
3257 const gpa = zcu.gpa;
3258 for (zcu.retryable_failures.items) |depender| {
3259 if (zcu.outdated.contains(depender)) continue;
3260 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| {
3261 // This Depender was already PO, but we now consider it outdated.
3262 // Any transitive dependencies are already marked PO.
3263 try zcu.outdated.put(gpa, depender, kv.value);
3264 continue;
3265 }
3266 // This Depender was not marked PO, but is now outdated. Mark it as
3267 // such, then recursively mark transitive dependencies as PO.
3268 try zcu.outdated.put(gpa, depender, 0);
3269 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
3270 }
3271 zcu.retryable_failures.clearRetainingCapacity();
3272}
3273
3274pub fn mapOldZirToNew(
3275 gpa: Allocator,
3276 old_zir: Zir,
3277 new_zir: Zir,
3278 inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
3279) Allocator.Error!void {
3280 // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc.
3281 // Not `declaration`, as this does not create a namespace.
3282 const MatchedZirDecl = struct {
3283 old_inst: Zir.Inst.Index,
3284 new_inst: Zir.Inst.Index,
3285 };
3286 var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{};
3287 defer match_stack.deinit(gpa);
3288
3289 // Main struct inst is always matched
3290 try match_stack.append(gpa, .{
3291 .old_inst = .main_struct_inst,
3292 .new_inst = .main_struct_inst,
3293 });
3294
3295 // Used as temporary buffers for namespace declaration instructions
3296 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
3297 defer old_decls.deinit();
3298 var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
3299 defer new_decls.deinit();
3300
3301 while (match_stack.popOrNull()) |match_item| {
3302 // Match the namespace declaration itself
3303 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);
3304
3305 // Maps decl name to `declaration` instruction.
3306 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{};
3307 defer named_decls.deinit(gpa);
3308 // Maps test name to `declaration` instruction.
3309 var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{};
3310 defer named_tests.deinit(gpa);
3311 // All unnamed tests, in order, for a best-effort match.
3312 var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
3313 defer unnamed_tests.deinit(gpa);
3314 // All comptime declarations, in order, for a best-effort match.
3315 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
3316 defer comptime_decls.deinit(gpa);
3317 // All usingnamespace declarations, in order, for a best-effort match.
3318 var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
3319 defer usingnamespace_decls.deinit(gpa);
3320
3321 {
3322 var old_decl_it = old_zir.declIterator(match_item.old_inst);
3323 while (old_decl_it.next()) |old_decl_inst| {
3324 const old_decl, _ = old_zir.getDeclaration(old_decl_inst);
3325 switch (old_decl.name) {
3326 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
3327 .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
3328 .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst),
3329 _ => {
3330 const name_nts = old_decl.name.toString(old_zir).?;
3331 const name = old_zir.nullTerminatedString(name_nts);
3332 if (old_decl.name.isNamedTest(old_zir)) {
3333 try named_tests.put(gpa, name, old_decl_inst);
3334 } else {
3335 try named_decls.put(gpa, name, old_decl_inst);
3336 }
3337 },
3338 }
3339 }
3340 }
3341
3342 var unnamed_test_idx: u32 = 0;
3343 var comptime_decl_idx: u32 = 0;
3344 var usingnamespace_decl_idx: u32 = 0;
3345
3346 var new_decl_it = new_zir.declIterator(match_item.new_inst);
3347 while (new_decl_it.next()) |new_decl_inst| {
3348 const new_decl, _ = new_zir.getDeclaration(new_decl_inst);
3349 // Attempt to match this to a declaration in the old ZIR:
3350 // * For named declarations (`const`/`var`/`fn`), we match based on name.
3351 // * For named tests (`test "foo"`), we also match based on name.
3352 // * For unnamed tests and decltests, we match based on order.
3353 // * For comptime blocks, we match based on order.
3354 // * For usingnamespace decls, we match based on order.
3355 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
3356 const old_decl_inst = switch (new_decl.name) {
3357 .@"comptime" => inst: {
3358 if (comptime_decl_idx == comptime_decls.items.len) continue;
3359 defer comptime_decl_idx += 1;
3360 break :inst comptime_decls.items[comptime_decl_idx];
3361 },
3362 .@"usingnamespace" => inst: {
3363 if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue;
3364 defer usingnamespace_decl_idx += 1;
3365 break :inst usingnamespace_decls.items[usingnamespace_decl_idx];
3366 },
3367 .unnamed_test, .decltest => inst: {
3368 if (unnamed_test_idx == unnamed_tests.items.len) continue;
3369 defer unnamed_test_idx += 1;
3370 break :inst unnamed_tests.items[unnamed_test_idx];
3371 },
3372 _ => inst: {
3373 const name_nts = new_decl.name.toString(old_zir).?;
3374 const name = new_zir.nullTerminatedString(name_nts);
3375 if (new_decl.name.isNamedTest(new_zir)) {
3376 break :inst named_tests.get(name) orelse continue;
3377 } else {
3378 break :inst named_decls.get(name) orelse continue;
3379 }
3380 },
3381 };
3382
3383 // Match the `declaration` instruction
3384 try inst_map.put(gpa, old_decl_inst, new_decl_inst);
3385
3386 // Find namespace declarations within this declaration
3387 try old_zir.findDecls(&old_decls, old_decl_inst);
3388 try new_zir.findDecls(&new_decls, new_decl_inst);
3389
3390 // We don't have any smart way of matching up these namespace declarations, so we always
3391 // correlate them based on source order.
3392 const n = @min(old_decls.items.len, new_decls.items.len);
3393 try match_stack.ensureUnusedCapacity(gpa, n);
3394 for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| {
3395 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3396 }
3397 }
3398 }
3399}
3400
3401/// Like `ensureDeclAnalyzed`, but the Decl is a file's root Decl.
3402pub fn ensureFileAnalyzed(zcu: *Zcu, file: *File) SemaError!void {
3403 if (file.root_decl.unwrap()) |existing_root| {
3404 return zcu.ensureDeclAnalyzed(existing_root);
3405 } else {
3406 return zcu.semaFile(file);
3407 }
3408}
3409
3410/// This ensures that the Decl will have an up-to-date Type and Value populated.
3411/// However the resolution status of the Type may not be fully resolved.
3412/// For example an inferred error set is not resolved until after `analyzeFnBody`.
3413/// is called.
3414pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
3415 const tracy = trace(@src());
3416 defer tracy.end();
3417
3418 const ip = &mod.intern_pool;
3419 const decl = mod.declPtr(decl_index);
3420
3421 log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{
3422 @intFromEnum(decl_index),
3423 decl.name.fmt(ip),
3424 });
3425
3426 // Determine whether or not this Decl is outdated, i.e. requires re-analysis
3427 // even if `complete`. If a Decl is PO, we pessismistically assume that it
3428 // *does* require re-analysis, to ensure that the Decl is definitely
3429 // up-to-date when this function returns.
3430
3431 // If analysis occurs in a poor order, this could result in over-analysis.
3432 // We do our best to avoid this by the other dependency logic in this file
3433 // which tries to limit re-analysis to Decls whose previously listed
3434 // dependencies are all up-to-date.
3435
3436 const decl_as_depender = InternPool.Depender.wrap(.{ .decl = decl_index });
3437 const decl_was_outdated = mod.outdated.swapRemove(decl_as_depender) or
3438 mod.potentially_outdated.swapRemove(decl_as_depender);
3439
3440 if (decl_was_outdated) {
3441 _ = mod.outdated_ready.swapRemove(decl_as_depender);
3442 }
3443
3444 const was_outdated = mod.outdated_file_root.swapRemove(decl_index) or decl_was_outdated;
3445
3446 switch (decl.analysis) {
3447 .in_progress => unreachable,
3448
3449 .file_failure => return error.AnalysisFail,
3450
3451 .sema_failure,
3452 .dependency_failure,
3453 .codegen_failure,
3454 => if (!was_outdated) return error.AnalysisFail,
3455
3456 .complete => if (!was_outdated) return,
3457
3458 .unreferenced => {},
3459 }
3460
3461 if (was_outdated) {
3462 // The exports this Decl performs will be re-discovered, so we remove them here
3463 // prior to re-analysis.
3464 if (build_options.only_c) unreachable;
3465 try mod.deleteDeclExports(decl_index);
3466 }
3467
3468 const sema_result: SemaDeclResult = blk: {
3469 if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) {
3470 // Anonymous decl. We don't semantically analyze these.
3471 break :blk .{
3472 .invalidate_decl_val = false,
3473 .invalidate_decl_ref = false,
3474 };
3475 }
3476
3477 if (mod.declIsRoot(decl_index)) {
3478 const changed = try mod.semaFileUpdate(decl.getFileScope(mod), decl_was_outdated);
3479 break :blk .{
3480 .invalidate_decl_val = changed,
3481 .invalidate_decl_ref = changed,
3482 };
3483 }
3484
3485 const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
3486 defer decl_prog_node.end();
3487
3488 break :blk mod.semaDecl(decl_index) catch |err| switch (err) {
3489 error.AnalysisFail => {
3490 if (decl.analysis == .in_progress) {
3491 // If this decl caused the compile error, the analysis field would
3492 // be changed to indicate it was this Decl's fault. Because this
3493 // did not happen, we infer here that it was a dependency failure.
3494 decl.analysis = .dependency_failure;
3495 }
3496 return error.AnalysisFail;
3497 },
3498 error.GenericPoison => unreachable,
3499 else => |e| {
3500 decl.analysis = .sema_failure;
3501 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
3502 try mod.retryable_failures.append(mod.gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
3503 mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
3504 mod.gpa,
3505 decl.navSrcLoc(mod).upgrade(mod),
3506 "unable to analyze: {s}",
3507 .{@errorName(e)},
3508 ));
3509 return error.AnalysisFail;
3510 },
3511 };
3512 };
3513
3514 // TODO: we do not yet have separate dependencies for decl values vs types.
3515 if (decl_was_outdated) {
3516 if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) {
3517 log.debug("Decl tv invalidated ('{d}')", .{@intFromEnum(decl_index)});
3518 // This dependency was marked as PO, meaning dependees were waiting
3519 // on its analysis result, and it has turned out to be outdated.
3520 // Update dependees accordingly.
3521 try mod.markDependeeOutdated(.{ .decl_val = decl_index });
3522 } else {
3523 log.debug("Decl tv up-to-date ('{d}')", .{@intFromEnum(decl_index)});
3524 // This dependency was previously PO, but turned out to be up-to-date.
3525 // We do not need to queue successive analysis.
3526 try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index });
3527 }
3528 }
3529}
3530
3531pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.Index) SemaError!void {
3532 const tracy = trace(@src());
3533 defer tracy.end();
3534
3535 const gpa = zcu.gpa;
3536 const ip = &zcu.intern_pool;
3537
3538 // We only care about the uncoerced function.
3539 // We need to do this for the "orphaned function" check below to be valid.
3540 const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
3541
3542 const func = zcu.funcInfo(maybe_coerced_func_index);
3543 const decl_index = func.owner_decl;
3544 const decl = zcu.declPtr(decl_index);
3545
3546 log.debug("ensureFuncBodyAnalyzed '{d}' (instance of '{}')", .{
3547 @intFromEnum(func_index),
3548 decl.name.fmt(ip),
3549 });
3550
3551 // First, our owner decl must be up-to-date. This will always be the case
3552 // during the first update, but may not on successive updates if we happen
3553 // to get analyzed before our parent decl.
3554 try zcu.ensureDeclAnalyzed(decl_index);
3555
3556 // On an update, it's possible this function changed such that our owner
3557 // decl now refers to a different function, making this one orphaned. If
3558 // that's the case, we should remove this function from the binary.
3559 if (decl.val.ip_index != func_index) {
3560 try zcu.markDependeeOutdated(.{ .func_ies = func_index });
3561 ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
3562 ip.remove(func_index);
3563 @panic("TODO: remove orphaned function from binary");
3564 }
3565
3566 // We'll want to remember what the IES used to be before the update for
3567 // dependency invalidation purposes.
3568 const old_resolved_ies = if (func.analysis(ip).inferred_error_set)
3569 func.resolvedErrorSet(ip).*
3570 else
3571 .none;
3572
3573 switch (decl.analysis) {
3574 .unreferenced => unreachable,
3575 .in_progress => unreachable,
3576
3577 .codegen_failure => unreachable, // functions do not perform constant value generation
3578
3579 .file_failure,
3580 .sema_failure,
3581 .dependency_failure,
3582 => return error.AnalysisFail,
3583
3584 .complete => {},
3585 }
3586
3587 const func_as_depender = InternPool.Depender.wrap(.{ .func = func_index });
3588 const was_outdated = zcu.outdated.swapRemove(func_as_depender) or
3589 zcu.potentially_outdated.swapRemove(func_as_depender);
3590
3591 if (was_outdated) {
3592 _ = zcu.outdated_ready.swapRemove(func_as_depender);
3593 }
3594
3595 switch (func.analysis(ip).state) {
3596 .success => if (!was_outdated) return,
3597 .sema_failure,
3598 .dependency_failure,
3599 .codegen_failure,
3600 => if (!was_outdated) return error.AnalysisFail,
3601 .none, .queued => {},
3602 .in_progress => unreachable,
3603 .inline_only => unreachable, // don't queue work for this
3604 }
3605
3606 log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{
3607 @intFromEnum(func_index),
3608 if (was_outdated) "outdated" else "never analyzed",
3609 });
3610
3611 var tmp_arena = std.heap.ArenaAllocator.init(gpa);
3612 defer tmp_arena.deinit();
3613 const sema_arena = tmp_arena.allocator();
3614
3615 var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) {
3616 error.AnalysisFail => {
3617 if (func.analysis(ip).state == .in_progress) {
3618 // If this decl caused the compile error, the analysis field would
3619 // be changed to indicate it was this Decl's fault. Because this
3620 // did not happen, we infer here that it was a dependency failure.
3621 func.analysis(ip).state = .dependency_failure;
3622 }
3623 return error.AnalysisFail;
3624 },
3625 error.OutOfMemory => return error.OutOfMemory,
3626 };
3627 defer air.deinit(gpa);
3628
3629 const invalidate_ies_deps = i: {
3630 if (!was_outdated) break :i false;
3631 if (!func.analysis(ip).inferred_error_set) break :i true;
3632 const new_resolved_ies = func.resolvedErrorSet(ip).*;
3633 break :i new_resolved_ies != old_resolved_ies;
3634 };
3635 if (invalidate_ies_deps) {
3636 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
3637 try zcu.markDependeeOutdated(.{ .func_ies = func_index });
3638 } else if (was_outdated) {
3639 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
3640 try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index });
3641 }
3642
3643 const comp = zcu.comp;
3644
3645 const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
3646 const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null);
3647
3648 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
3649 return;
3650 }
3651
3652 var liveness = try Liveness.analyze(gpa, air, ip);
3653 defer liveness.deinit(gpa);
3654
3655 if (dump_air) {
3656 const fqn = try decl.fullyQualifiedName(zcu);
3657 std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)});
3658 @import("print_air.zig").dump(zcu, air, liveness);
3659 std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)});
3660 }
3661
3662 if (std.debug.runtime_safety) {
3663 var verify = Liveness.Verify{
3664 .gpa = gpa,
3665 .air = air,
3666 .liveness = liveness,
3667 .intern_pool = ip,
3668 };
3669 defer verify.deinit();
3670
3671 verify.verify() catch |err| switch (err) {
3672 error.OutOfMemory => return error.OutOfMemory,
3673 else => {
3674 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
3675 zcu.failed_decls.putAssumeCapacityNoClobber(
3676 decl_index,
3677 try Module.ErrorMsg.create(
3678 gpa,
3679 decl.navSrcLoc(zcu).upgrade(zcu),
3680 "invalid liveness: {s}",
3681 .{@errorName(err)},
3682 ),
3683 );
3684 func.analysis(ip).state = .codegen_failure;
3685 return;
3686 },
3687 };
3688 }
3689
3690 const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(ip), 0);
3691 defer codegen_prog_node.end();
3692
3693 if (comp.bin_file) |lf| {
3694 lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
3695 error.OutOfMemory => return error.OutOfMemory,
3696 error.AnalysisFail => {
3697 func.analysis(ip).state = .codegen_failure;
3698 },
3699 else => {
3700 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
3701 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
3702 gpa,
3703 decl.navSrcLoc(zcu).upgrade(zcu),
3704 "unable to codegen: {s}",
3705 .{@errorName(err)},
3706 ));
3707 func.analysis(ip).state = .codegen_failure;
3708 try zcu.retryable_failures.append(zcu.gpa, InternPool.Depender.wrap(.{ .func = func_index }));
3709 },
3710 };
3711 } else if (zcu.llvm_object) |llvm_object| {
3712 if (build_options.only_c) unreachable;
3713 llvm_object.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
3714 error.OutOfMemory => return error.OutOfMemory,
3715 error.AnalysisFail => {
3716 func.analysis(ip).state = .codegen_failure;
3717 },
3718 };
3719 }
3720}
3721
3722/// Ensure this function's body is or will be analyzed and emitted. This should
3723/// be called whenever a potential runtime call of a function is seen.
3724///
3725/// The caller is responsible for ensuring the function decl itself is already
3726/// analyzed, and for ensuring it can exist at runtime (see
3727/// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body
3728/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
3729pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) !void {
3730 const ip = &mod.intern_pool;
3731 const func = mod.funcInfo(func_index);
3732 const decl_index = func.owner_decl;
3733 const decl = mod.declPtr(decl_index);
3734
3735 switch (decl.analysis) {
3736 .unreferenced => unreachable,
3737 .in_progress => unreachable,
3738
3739 .file_failure,
3740 .sema_failure,
3741 .codegen_failure,
3742 .dependency_failure,
3743 // Analysis of the function Decl itself failed, but we've already
3744 // emitted an error for that. The callee doesn't need the function to be
3745 // analyzed right now, so its analysis can safely continue.
3746 => return,
3747
3748 .complete => {},
3749 }
3750
3751 assert(decl.has_tv);
3752
3753 const func_as_depender = InternPool.Depender.wrap(.{ .func = func_index });
3754 const is_outdated = mod.outdated.contains(func_as_depender) or
3755 mod.potentially_outdated.contains(func_as_depender);
3756
3757 switch (func.analysis(ip).state) {
3758 .none => {},
3759 .queued => return,
3760 // As above, we don't need to forward errors here.
3761 .sema_failure,
3762 .dependency_failure,
3763 .codegen_failure,
3764 .success,
3765 => if (!is_outdated) return,
3766 .in_progress => return,
3767 .inline_only => unreachable, // don't queue work for this
3768 }
3769
3770 // Decl itself is safely analyzed, and body analysis is not yet queued
3771
3772 try mod.comp.work_queue.writeItem(.{ .codegen_func = func_index });
3773 if (mod.emit_h != null) {
3774 // TODO: we ideally only want to do this if the function's type changed
3775 // since the last update
3776 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
3777 }
3778 func.analysis(ip).state = .queued;
3779}
3780
3781/// https://github.com/ziglang/zig/issues/14307
3782pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void {
3783 const file = (try mod.importPkg(pkg)).file;
3784 if (file.root_decl == .none) {
3785 return mod.semaFile(file);
3786 }
3787}
3788
3789fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index {
3790 const gpa = zcu.gpa;
3791 const ip = &zcu.intern_pool;
3792 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
3793 assert(extended.opcode == .struct_decl);
3794 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3795 assert(!small.has_captures_len);
3796 assert(!small.has_backing_int);
3797 assert(small.layout == .auto);
3798 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
3799 const fields_len = if (small.has_fields_len) blk: {
3800 const fields_len = file.zir.extra[extra_index];
3801 extra_index += 1;
3802 break :blk fields_len;
3803 } else 0;
3804 const decls_len = if (small.has_decls_len) blk: {
3805 const decls_len = file.zir.extra[extra_index];
3806 extra_index += 1;
3807 break :blk decls_len;
3808 } else 0;
3809 const decls = file.zir.bodySlice(extra_index, decls_len);
3810 extra_index += decls_len;
3811
3812 const tracked_inst = try ip.trackZir(gpa, file, .main_struct_inst);
3813 const wip_ty = switch (try ip.getStructType(gpa, .{
3814 .layout = .auto,
3815 .fields_len = fields_len,
3816 .known_non_opv = small.known_non_opv,
3817 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
3818 .is_tuple = small.is_tuple,
3819 .any_comptime_fields = small.any_comptime_fields,
3820 .any_default_inits = small.any_default_inits,
3821 .inits_resolved = false,
3822 .any_aligned_fields = small.any_aligned_fields,
3823 .has_namespace = true,
3824 .key = .{ .declared = .{
3825 .zir_index = tracked_inst,
3826 .captures = &.{},
3827 } },
3828 })) {
3829 .existing => unreachable, // we wouldn't be analysing the file root if this type existed
3830 .wip => |wip| wip,
3831 };
3832 errdefer wip_ty.cancel(ip);
3833
3834 if (zcu.comp.debug_incremental) {
3835 try ip.addDependency(
3836 gpa,
3837 InternPool.Depender.wrap(.{ .decl = decl_index }),
3838 .{ .src_hash = tracked_inst },
3839 );
3840 }
3841
3842 const decl = zcu.declPtr(decl_index);
3843 decl.val = Value.fromInterned(wip_ty.index);
3844 decl.has_tv = true;
3845 decl.owns_tv = true;
3846 decl.analysis = .complete;
3847
3848 try zcu.scanNamespace(namespace_index, decls, decl);
3849
3850 return wip_ty.finish(ip, decl_index, namespace_index.toOptional());
3851}
3852
3853/// Re-analyze the root Decl of a file on an incremental update.
3854/// If `type_outdated`, the struct type itself is considered outdated and is
3855/// reconstructed at a new InternPool index. Otherwise, the namespace is just
3856/// re-analyzed. Returns whether the decl's tyval was invalidated.
3857fn semaFileUpdate(zcu: *Zcu, file: *File, type_outdated: bool) SemaError!bool {
3858 const decl = zcu.declPtr(file.root_decl.unwrap().?);
3859
3860 log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{
3861 file.mod.fully_qualified_name,
3862 file.sub_file_path,
3863 type_outdated,
3864 });
3865
3866 if (file.status != .success_zir) {
3867 if (decl.analysis == .file_failure) {
3868 return false;
3869 } else {
3870 decl.analysis = .file_failure;
3871 return true;
3872 }
3873 }
3874
3875 if (decl.analysis == .file_failure) {
3876 // No struct type currently exists. Create one!
3877 _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file);
3878 return true;
3879 }
3880
3881 assert(decl.has_tv);
3882 assert(decl.owns_tv);
3883
3884 if (type_outdated) {
3885 // Invalidate the existing type, reusing the decl and namespace.
3886 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = file.root_decl.unwrap().? }));
3887 zcu.intern_pool.remove(decl.val.toIntern());
3888 decl.val = undefined;
3889 _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file);
3890 return true;
3891 }
3892
3893 // Only the struct's namespace is outdated.
3894 // Preserve the type - just scan the namespace again.
3895
3896 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
3897 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3898
3899 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
3900 extra_index += @intFromBool(small.has_fields_len);
3901 const decls_len = if (small.has_decls_len) blk: {
3902 const decls_len = file.zir.extra[extra_index];
3903 extra_index += 1;
3904 break :blk decls_len;
3905 } else 0;
3906 const decls = file.zir.bodySlice(extra_index, decls_len);
3907
3908 if (!type_outdated) {
3909 try zcu.scanNamespace(decl.src_namespace, decls, decl);
3910 }
3911
3912 return false;
3913}
3914
3915/// Regardless of the file status, will create a `Decl` if none exists so that we can track
3916/// dependencies and re-analyze when the file becomes outdated.
3917fn semaFile(mod: *Module, file: *File) SemaError!void {
3918 const tracy = trace(@src());
3919 defer tracy.end();
3920
3921 assert(file.root_decl == .none);
3922
3923 const gpa = mod.gpa;
3924 log.debug("semaFile mod={s} sub_file_path={s}", .{
3925 file.mod.fully_qualified_name, file.sub_file_path,
3926 });
3927
3928 // Because these three things each reference each other, `undefined`
3929 // placeholders are used before being set after the struct type gains an
3930 // InternPool index.
3931 const new_namespace_index = try mod.createNamespace(.{
3932 .parent = .none,
3933 .decl_index = undefined,
3934 .file_scope = file,
3935 });
3936 errdefer mod.destroyNamespace(new_namespace_index);
3937
3938 const new_decl_index = try mod.allocateNewDecl(new_namespace_index);
3939 const new_decl = mod.declPtr(new_decl_index);
3940 errdefer @panic("TODO error handling");
3941
3942 file.root_decl = new_decl_index.toOptional();
3943 mod.namespacePtr(new_namespace_index).decl_index = new_decl_index;
3944
3945 new_decl.name = try file.fullyQualifiedName(mod);
3946 new_decl.name_fully_qualified = true;
3947 new_decl.src_line = 0;
3948 new_decl.is_pub = true;
3949 new_decl.is_exported = false;
3950 new_decl.alignment = .none;
3951 new_decl.@"linksection" = .none;
3952 new_decl.analysis = .in_progress;
3953
3954 if (file.status != .success_zir) {
3955 new_decl.analysis = .file_failure;
3956 return;
3957 }
3958 assert(file.zir_loaded);
3959
3960 const struct_ty = try mod.getFileRootStruct(new_decl_index, new_namespace_index, file);
3961 errdefer mod.intern_pool.remove(struct_ty);
3962
3963 switch (mod.comp.cache_use) {
3964 .whole => |whole| if (whole.cache_manifest) |man| {
3965 const source = file.getSource(gpa) catch |err| {
3966 try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)});
3967 return error.AnalysisFail;
3968 };
3969
3970 const resolved_path = std.fs.path.resolve(gpa, &.{
3971 file.mod.root.root_dir.path orelse ".",
3972 file.mod.root.sub_path,
3973 file.sub_file_path,
3974 }) catch |err| {
3975 try reportRetryableFileError(mod, file, "unable to resolve path: {s}", .{@errorName(err)});
3976 return error.AnalysisFail;
3977 };
3978 errdefer gpa.free(resolved_path);
3979
3980 whole.cache_manifest_mutex.lock();
3981 defer whole.cache_manifest_mutex.unlock();
3982 try man.addFilePostContents(resolved_path, source.bytes, source.stat);
3983 },
3984 .incremental => {},
3985 }
3986}
3987
3988const SemaDeclResult = packed struct {
3989 /// Whether the value of a `decl_val` of this Decl changed.
3990 invalidate_decl_val: bool,
3991 /// Whether the type of a `decl_ref` of this Decl changed.
3992 invalidate_decl_ref: bool,
3993};
3994
3995fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
3996 const tracy = trace(@src());
3997 defer tracy.end();
3998
3999 const decl = mod.declPtr(decl_index);
4000 const ip = &mod.intern_pool;
4001
4002 if (decl.getFileScope(mod).status != .success_zir) {
4003 return error.AnalysisFail;
4004 }
4005
4006 assert(!mod.declIsRoot(decl_index));
4007
4008 if (decl.zir_decl_index == .none and decl.owns_tv) {
4009 // We are re-analyzing an anonymous owner Decl (for a function or a namespace type).
4010 return mod.semaAnonOwnerDecl(decl_index);
4011 }
4012
4013 log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)});
4014 log.debug("decl name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)});
4015 defer blk: {
4016 log.debug("finish decl name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});
4017 }
4018
4019 const old_has_tv = decl.has_tv;
4020 // The following values are ignored if `!old_has_tv`
4021 const old_ty = if (old_has_tv) decl.typeOf(mod) else undefined;
4022 const old_val = decl.val;
4023 const old_align = decl.alignment;
4024 const old_linksection = decl.@"linksection";
4025 const old_addrspace = decl.@"addrspace";
4026 const old_is_inline = if (decl.getOwnedFunction(mod)) |prev_func|
4027 prev_func.analysis(ip).state == .inline_only
4028 else
4029 false;
4030
4031 const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip);
4032
4033 const gpa = mod.gpa;
4034 const zir = decl.getFileScope(mod).zir;
4035
4036 const builtin_type_target_index: InternPool.Index = ip_index: {
4037 const std_mod = mod.std_mod;
4038 if (decl.getFileScope(mod).mod != std_mod) break :ip_index .none;
4039 // We're in the std module.
4040 const std_file = (try mod.importPkg(std_mod)).file;
4041 const std_decl = mod.declPtr(std_file.root_decl.unwrap().?);
4042 const std_namespace = std_decl.getInnerNamespace(mod).?;
4043 const builtin_str = try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls);
4044 const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .zcu = mod }) orelse break :ip_index .none);
4045 const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :ip_index .none;
4046 if (decl.src_namespace != builtin_namespace) break :ip_index .none;
4047 // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index.
4048 for ([_][]const u8{
4049 "AtomicOrder",
4050 "AtomicRmwOp",
4051 "CallingConvention",
4052 "AddressSpace",
4053 "FloatMode",
4054 "ReduceOp",
4055 "CallModifier",
4056 "PrefetchOptions",
4057 "ExportOptions",
4058 "ExternOptions",
4059 "Type",
4060 }, [_]InternPool.Index{
4061 .atomic_order_type,
4062 .atomic_rmw_op_type,
4063 .calling_convention_type,
4064 .address_space_type,
4065 .float_mode_type,
4066 .reduce_op_type,
4067 .call_modifier_type,
4068 .prefetch_options_type,
4069 .export_options_type,
4070 .extern_options_type,
4071 .type_info_type,
4072 }) |type_name, type_ip| {
4073 if (decl.name.eqlSlice(type_name, ip)) break :ip_index type_ip;
4074 }
4075 break :ip_index .none;
4076 };
4077
4078 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
4079
4080 decl.analysis = .in_progress;
4081
4082 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
4083 defer analysis_arena.deinit();
4084
4085 var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
4086 defer comptime_err_ret_trace.deinit();
4087
4088 var sema: Sema = .{
4089 .mod = mod,
4090 .gpa = gpa,
4091 .arena = analysis_arena.allocator(),
4092 .code = zir,
4093 .owner_decl = decl,
4094 .owner_decl_index = decl_index,
4095 .func_index = .none,
4096 .func_is_naked = false,
4097 .fn_ret_ty = Type.void,
4098 .fn_ret_ty_ies = null,
4099 .owner_func_index = .none,
4100 .comptime_err_ret_trace = &comptime_err_ret_trace,
4101 .builtin_type_target_index = builtin_type_target_index,
4102 };
4103 defer sema.deinit();
4104
4105 // Every Decl (other than file root Decls, which do not have a ZIR index) has a dependency on its own source.
4106 try sema.declareDependency(.{ .src_hash = try ip.trackZir(
4107 sema.gpa,
4108 decl.getFileScope(mod),
4109 decl_inst,
4110 ) });
4111
4112 var block_scope: Sema.Block = .{
4113 .parent = null,
4114 .sema = &sema,
4115 .namespace = decl.src_namespace,
4116 .instructions = .{},
4117 .inlining = null,
4118 .is_comptime = true,
4119 .src_base_inst = decl.zir_decl_index.unwrap().?,
4120 .type_name_ctx = decl.name,
4121 };
4122 defer block_scope.instructions.deinit(gpa);
4123
4124 const decl_bodies = decl.zirBodies(mod);
4125
4126 const result_ref = try sema.resolveInlineBody(&block_scope, decl_bodies.value_body, decl_inst);
4127 // We'll do some other bits with the Sema. Clear the type target index just
4128 // in case they analyze any type.
4129 sema.builtin_type_target_index = .none;
4130 const align_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_align = 0 });
4131 const section_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_section = 0 });
4132 const address_space_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_addrspace = 0 });
4133 const ty_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_ty = 0 });
4134 const init_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_init = 0 });
4135 const decl_val = try sema.resolveFinalDeclValue(&block_scope, init_src, result_ref);
4136 const decl_ty = decl_val.typeOf(mod);
4137
4138 // Note this resolves the type of the Decl, not the value; if this Decl
4139 // is a struct, for example, this resolves `type` (which needs no resolution),
4140 // not the struct itself.
4141 try sema.resolveTypeLayout(decl_ty);
4142
4143 if (decl.kind == .@"usingnamespace") {
4144 if (!decl_ty.eql(Type.type, mod)) {
4145 return sema.fail(&block_scope, ty_src, "expected type, found {}", .{
4146 decl_ty.fmt(mod),
4147 });
4148 }
4149 const ty = decl_val.toType();
4150 if (ty.getNamespace(mod) == null) {
4151 return sema.fail(&block_scope, ty_src, "type {} has no namespace", .{ty.fmt(mod)});
4152 }
4153
4154 decl.val = ty.toValue();
4155 decl.alignment = .none;
4156 decl.@"linksection" = .none;
4157 decl.has_tv = true;
4158 decl.owns_tv = false;
4159 decl.analysis = .complete;
4160
4161 // TODO: usingnamespace cannot currently participate in incremental compilation
4162 return .{
4163 .invalidate_decl_val = true,
4164 .invalidate_decl_ref = true,
4165 };
4166 }
4167
4168 var queue_linker_work = true;
4169 var is_func = false;
4170 var is_inline = false;
4171 switch (decl_val.toIntern()) {
4172 .generic_poison => unreachable,
4173 .unreachable_value => unreachable,
4174 else => switch (ip.indexToKey(decl_val.toIntern())) {
4175 .variable => |variable| {
4176 decl.owns_tv = variable.decl == decl_index;
4177 queue_linker_work = decl.owns_tv;
4178 },
4179
4180 .extern_func => |extern_func| {
4181 decl.owns_tv = extern_func.decl == decl_index;
4182 queue_linker_work = decl.owns_tv;
4183 is_func = decl.owns_tv;
4184 },
4185
4186 .func => |func| {
4187 decl.owns_tv = func.owner_decl == decl_index;
4188 queue_linker_work = false;
4189 is_inline = decl.owns_tv and decl_ty.fnCallingConvention(mod) == .Inline;
4190 is_func = decl.owns_tv;
4191 },
4192
4193 else => {},
4194 },
4195 }
4196
4197 decl.val = decl_val;
4198 // Function linksection, align, and addrspace were already set by Sema
4199 if (!is_func) {
4200 decl.alignment = blk: {
4201 const align_body = decl_bodies.align_body orelse break :blk .none;
4202 const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst);
4203 break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
4204 };
4205 decl.@"linksection" = blk: {
4206 const linksection_body = decl_bodies.linksection_body orelse break :blk .none;
4207 const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst);
4208 const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{
4209 .needed_comptime_reason = "linksection must be comptime-known",
4210 });
4211 if (mem.indexOfScalar(u8, bytes, 0) != null) {
4212 return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{});
4213 } else if (bytes.len == 0) {
4214 return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
4215 }
4216 break :blk try ip.getOrPutStringOpt(gpa, bytes, .no_embedded_nulls);
4217 };
4218 decl.@"addrspace" = blk: {
4219 const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_val.toIntern())) {
4220 .variable => .variable,
4221 .extern_func, .func => .function,
4222 else => .constant,
4223 };
4224
4225 const target = sema.mod.getTarget();
4226
4227 const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) {
4228 .function => target_util.defaultAddressSpace(target, .function),
4229 .variable => target_util.defaultAddressSpace(target, .global_mutable),
4230 .constant => target_util.defaultAddressSpace(target, .global_constant),
4231 else => unreachable,
4232 };
4233 const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst);
4234 break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx);
4235 };
4236 }
4237 decl.has_tv = true;
4238 decl.analysis = .complete;
4239
4240 const result: SemaDeclResult = if (old_has_tv) .{
4241 .invalidate_decl_val = !decl_ty.eql(old_ty, mod) or
4242 !decl.val.eql(old_val, decl_ty, mod) or
4243 is_inline != old_is_inline,
4244 .invalidate_decl_ref = !decl_ty.eql(old_ty, mod) or
4245 decl.alignment != old_align or
4246 decl.@"linksection" != old_linksection or
4247 decl.@"addrspace" != old_addrspace or
4248 is_inline != old_is_inline,
4249 } else .{
4250 .invalidate_decl_val = true,
4251 .invalidate_decl_ref = true,
4252 };
4253
4254 const has_runtime_bits = queue_linker_work and (is_func or try sema.typeHasRuntimeBits(decl_ty));
4255 if (has_runtime_bits) {
4256 // Needed for codegen_decl which will call updateDecl and then the
4257 // codegen backend wants full access to the Decl Type.
4258 try sema.resolveTypeFully(decl_ty);
4259
4260 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
4261
4262 if (result.invalidate_decl_ref and mod.emit_h != null) {
4263 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
4264 }
4265 }
4266
4267 if (decl.is_exported) {
4268 const export_src: LazySrcLoc = block_scope.src(.{ .token_offset = @intFromBool(decl.is_pub) });
4269 if (is_inline) return sema.fail(&block_scope, export_src, "export of inline function", .{});
4270 // The scope needs to have the decl in it.
4271 try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index);
4272 }
4273
4274 return result;
4275}
4276
4277fn semaAnonOwnerDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult {
4278 const decl = zcu.declPtr(decl_index);
4279
4280 assert(decl.has_tv);
4281 assert(decl.owns_tv);
4282
4283 log.debug("semaAnonOwnerDecl '{d}'", .{@intFromEnum(decl_index)});
4284
4285 switch (decl.typeOf(zcu).zigTypeTag(zcu)) {
4286 .Fn => @panic("TODO: update fn instance"),
4287 .Type => {},
4288 else => unreachable,
4289 }
4290
4291 // We are the owner Decl of a type, and we were marked as outdated. That means the *structure*
4292 // of this type changed; not just its namespace. Therefore, we need a new InternPool index.
4293 //
4294 // However, as soon as we make that, the context that created us will require re-analysis anyway
4295 // (as it depends on this Decl's value), meaning the `struct_decl` (or equivalent) instruction
4296 // will be analyzed again. Since Sema already needs to be able to reconstruct types like this,
4297 // why should we bother implementing it here too when the Sema logic will be hit right after?
4298 //
4299 // So instead, let's just mark this Decl as failed - so that any remaining Decls which genuinely
4300 // reference it (via `@This`) end up silently erroring too - and we'll let Sema make a new type
4301 // with a new Decl.
4302 //
4303 // Yes, this does mean that any type owner Decl has a constant value for its entire lifetime.
4304 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
4305 zcu.intern_pool.remove(decl.val.toIntern());
4306 decl.analysis = .dependency_failure;
4307 return .{
4308 .invalidate_decl_val = true,
4309 .invalidate_decl_ref = true,
4310 };
4311}
4312
4313pub const ImportFileResult = struct {
4314 file: *File,
4315 is_new: bool,
4316 is_pkg: bool,
4317};
4318
4319pub fn importPkg(zcu: *Zcu, mod: *Package.Module) !ImportFileResult {
4320 const gpa = zcu.gpa;
4321
4322 // The resolved path is used as the key in the import table, to detect if
4323 // an import refers to the same as another, despite different relative paths
4324 // or differently mapped package names.
4325 const resolved_path = try std.fs.path.resolve(gpa, &.{
4326 mod.root.root_dir.path orelse ".",
4327 mod.root.sub_path,
4328 mod.root_src_path,
4329 });
4330 var keep_resolved_path = false;
4331 defer if (!keep_resolved_path) gpa.free(resolved_path);
4332
4333 const gop = try zcu.import_table.getOrPut(gpa, resolved_path);
4334 errdefer _ = zcu.import_table.pop();
4335 if (gop.found_existing) {
4336 try gop.value_ptr.*.addReference(zcu.*, .{ .root = mod });
4337 return ImportFileResult{
4338 .file = gop.value_ptr.*,
4339 .is_new = false,
4340 .is_pkg = true,
4341 };
4342 }
4343
4344 if (mod.builtin_file) |builtin_file| {
4345 keep_resolved_path = true; // It's now owned by import_table.
4346 gop.value_ptr.* = builtin_file;
4347 try builtin_file.addReference(zcu.*, .{ .root = mod });
4348 try zcu.path_digest_map.put(gpa, builtin_file.path_digest, {});
4349 return .{
4350 .file = builtin_file,
4351 .is_new = false,
4352 .is_pkg = true,
4353 };
4354 }
4355
4356 const sub_file_path = try gpa.dupe(u8, mod.root_src_path);
4357 errdefer gpa.free(sub_file_path);
4358
4359 const new_file = try gpa.create(File);
4360 errdefer gpa.destroy(new_file);
4361
4362 keep_resolved_path = true; // It's now owned by import_table.
4363 gop.value_ptr.* = new_file;
4364 new_file.* = .{
4365 .sub_file_path = sub_file_path,
4366 .source = undefined,
4367 .source_loaded = false,
4368 .tree_loaded = false,
4369 .zir_loaded = false,
4370 .stat = undefined,
4371 .tree = undefined,
4372 .zir = undefined,
4373 .status = .never_loaded,
4374 .mod = mod,
4375 .root_decl = .none,
4376 .path_digest = digest: {
4377 const want_local_cache = mod == zcu.main_mod;
4378 var path_hash: Cache.HashHelper = .{};
4379 path_hash.addBytes(build_options.version);
4380 path_hash.add(builtin.zig_backend);
4381 if (!want_local_cache) {
4382 path_hash.addOptionalBytes(mod.root.root_dir.path);
4383 path_hash.addBytes(mod.root.sub_path);
4384 }
4385 path_hash.addBytes(sub_file_path);
4386 var bin: Cache.BinDigest = undefined;
4387 path_hash.hasher.final(&bin);
4388 break :digest bin;
4389 },
4390 };
4391 try new_file.addReference(zcu.*, .{ .root = mod });
4392 try zcu.path_digest_map.put(gpa, new_file.path_digest, {});
4393 return ImportFileResult{
4394 .file = new_file,
4395 .is_new = true,
4396 .is_pkg = true,
4397 };
4398}
4399
4400pub fn importFile(
4401 zcu: *Zcu,
4402 cur_file: *File,
4403 import_string: []const u8,
4404) !ImportFileResult {
4405 if (std.mem.eql(u8, import_string, "std")) {
4406 return zcu.importPkg(zcu.std_mod);
4407 }
4408 if (std.mem.eql(u8, import_string, "root")) {
4409 return zcu.importPkg(zcu.root_mod);
4410 }
4411 if (cur_file.mod.deps.get(import_string)) |pkg| {
4412 return zcu.importPkg(pkg);
4413 }
4414 if (!mem.endsWith(u8, import_string, ".zig")) {
4415 return error.ModuleNotFound;
4416 }
4417 const gpa = zcu.gpa;
4418
4419 // The resolved path is used as the key in the import table, to detect if
4420 // an import refers to the same as another, despite different relative paths
4421 // or differently mapped package names.
4422 const resolved_path = try std.fs.path.resolve(gpa, &.{
4423 cur_file.mod.root.root_dir.path orelse ".",
4424 cur_file.mod.root.sub_path,
4425 cur_file.sub_file_path,
4426 "..",
4427 import_string,
4428 });
4429
4430 var keep_resolved_path = false;
4431 defer if (!keep_resolved_path) gpa.free(resolved_path);
4432
4433 const gop = try zcu.import_table.getOrPut(gpa, resolved_path);
4434 errdefer _ = zcu.import_table.pop();
4435 if (gop.found_existing) return ImportFileResult{
4436 .file = gop.value_ptr.*,
4437 .is_new = false,
4438 .is_pkg = false,
4439 };
4440
4441 const new_file = try gpa.create(File);
4442 errdefer gpa.destroy(new_file);
4443
4444 const resolved_root_path = try std.fs.path.resolve(gpa, &.{
4445 cur_file.mod.root.root_dir.path orelse ".",
4446 cur_file.mod.root.sub_path,
4447 });
4448 defer gpa.free(resolved_root_path);
4449
4450 const sub_file_path = p: {
4451 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
4452 errdefer gpa.free(relative);
4453
4454 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
4455 break :p relative;
4456 }
4457 return error.ImportOutsideModulePath;
4458 };
4459 errdefer gpa.free(sub_file_path);
4460
4461 log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{
4462 resolved_root_path, resolved_path, sub_file_path, import_string,
4463 });
4464
4465 keep_resolved_path = true; // It's now owned by import_table.
4466 gop.value_ptr.* = new_file;
4467 new_file.* = .{
4468 .sub_file_path = sub_file_path,
4469 .source = undefined,
4470 .source_loaded = false,
4471 .tree_loaded = false,
4472 .zir_loaded = false,
4473 .stat = undefined,
4474 .tree = undefined,
4475 .zir = undefined,
4476 .status = .never_loaded,
4477 .mod = cur_file.mod,
4478 .root_decl = .none,
4479 .path_digest = digest: {
4480 const want_local_cache = cur_file.mod == zcu.main_mod;
4481 var path_hash: Cache.HashHelper = .{};
4482 path_hash.addBytes(build_options.version);
4483 path_hash.add(builtin.zig_backend);
4484 if (!want_local_cache) {
4485 path_hash.addOptionalBytes(cur_file.mod.root.root_dir.path);
4486 path_hash.addBytes(cur_file.mod.root.sub_path);
4487 }
4488 path_hash.addBytes(sub_file_path);
4489 var bin: Cache.BinDigest = undefined;
4490 path_hash.hasher.final(&bin);
4491 break :digest bin;
4492 },
4493 };
4494 try zcu.path_digest_map.put(gpa, new_file.path_digest, {});
4495 return ImportFileResult{
4496 .file = new_file,
4497 .is_new = true,
4498 .is_pkg = false,
4499 };
4500}
4501
4502pub fn embedFile(
4503 mod: *Module,
4504 cur_file: *File,
4505 import_string: []const u8,
4506 src_loc: SrcLoc,
4507) !InternPool.Index {
4508 const gpa = mod.gpa;
4509
4510 if (cur_file.mod.deps.get(import_string)) |pkg| {
4511 const resolved_path = try std.fs.path.resolve(gpa, &.{
4512 pkg.root.root_dir.path orelse ".",
4513 pkg.root.sub_path,
4514 pkg.root_src_path,
4515 });
4516 var keep_resolved_path = false;
4517 defer if (!keep_resolved_path) gpa.free(resolved_path);
4518
4519 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4520 errdefer {
4521 assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path));
4522 keep_resolved_path = false;
4523 }
4524 if (gop.found_existing) return gop.value_ptr.*.val;
4525 keep_resolved_path = true;
4526
4527 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
4528 errdefer gpa.free(sub_file_path);
4529
4530 return newEmbedFile(mod, pkg, sub_file_path, resolved_path, gop.value_ptr, src_loc);
4531 }
4532
4533 // The resolved path is used as the key in the table, to detect if a file
4534 // refers to the same as another, despite different relative paths.
4535 const resolved_path = try std.fs.path.resolve(gpa, &.{
4536 cur_file.mod.root.root_dir.path orelse ".",
4537 cur_file.mod.root.sub_path,
4538 cur_file.sub_file_path,
4539 "..",
4540 import_string,
4541 });
4542
4543 var keep_resolved_path = false;
4544 defer if (!keep_resolved_path) gpa.free(resolved_path);
4545
4546 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4547 errdefer {
4548 assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path));
4549 keep_resolved_path = false;
4550 }
4551 if (gop.found_existing) return gop.value_ptr.*.val;
4552 keep_resolved_path = true;
4553
4554 const resolved_root_path = try std.fs.path.resolve(gpa, &.{
4555 cur_file.mod.root.root_dir.path orelse ".",
4556 cur_file.mod.root.sub_path,
4557 });
4558 defer gpa.free(resolved_root_path);
4559
4560 const sub_file_path = p: {
4561 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
4562 errdefer gpa.free(relative);
4563
4564 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
4565 break :p relative;
4566 }
4567 return error.ImportOutsideModulePath;
4568 };
4569 defer gpa.free(sub_file_path);
4570
4571 return newEmbedFile(mod, cur_file.mod, sub_file_path, resolved_path, gop.value_ptr, src_loc);
4572}
4573
4574/// https://github.com/ziglang/zig/issues/14307
4575fn newEmbedFile(
4576 mod: *Module,
4577 pkg: *Package.Module,
4578 sub_file_path: []const u8,
4579 resolved_path: []const u8,
4580 result: **EmbedFile,
4581 src_loc: SrcLoc,
4582) !InternPool.Index {
4583 const gpa = mod.gpa;
4584 const ip = &mod.intern_pool;
4585
4586 const new_file = try gpa.create(EmbedFile);
4587 errdefer gpa.destroy(new_file);
4588
4589 var file = try pkg.root.openFile(sub_file_path, .{});
4590 defer file.close();
4591
4592 const actual_stat = try file.stat();
4593 const stat: Cache.File.Stat = .{
4594 .size = actual_stat.size,
4595 .inode = actual_stat.inode,
4596 .mtime = actual_stat.mtime,
4597 };
4598 const size = std.math.cast(usize, actual_stat.size) orelse return error.Overflow;
4599
4600 const bytes = try ip.string_bytes.addManyAsSlice(gpa, try std.math.add(usize, size, 1));
4601 const actual_read = try file.readAll(bytes[0..size]);
4602 if (actual_read != size) return error.UnexpectedEndOfFile;
4603 bytes[size] = 0;
4604
4605 const comp = mod.comp;
4606 switch (comp.cache_use) {
4607 .whole => |whole| if (whole.cache_manifest) |man| {
4608 const copied_resolved_path = try gpa.dupe(u8, resolved_path);
4609 errdefer gpa.free(copied_resolved_path);
4610 whole.cache_manifest_mutex.lock();
4611 defer whole.cache_manifest_mutex.unlock();
4612 try man.addFilePostContents(copied_resolved_path, bytes[0..size], stat);
4613 },
4614 .incremental => {},
4615 }
4616
4617 const array_ty = try ip.get(gpa, .{ .array_type = .{
4618 .len = size,
4619 .sentinel = .zero_u8,
4620 .child = .u8_type,
4621 } });
4622 const array_val = try ip.get(gpa, .{ .aggregate = .{
4623 .ty = array_ty,
4624 .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, bytes.len, .maybe_embedded_nulls) },
4625 } });
4626
4627 const ptr_ty = (try mod.ptrType(.{
4628 .child = array_ty,
4629 .flags = .{
4630 .alignment = .none,
4631 .is_const = true,
4632 .address_space = .generic,
4633 },
4634 })).toIntern();
4635 const ptr_val = try ip.get(gpa, .{ .ptr = .{
4636 .ty = ptr_ty,
4637 .base_addr = .{ .anon_decl = .{
4638 .val = array_val,
4639 .orig_ty = ptr_ty,
4640 } },
4641 .byte_offset = 0,
4642 } });
4643
4644 result.* = new_file;
4645 new_file.* = .{
4646 .sub_file_path = try ip.getOrPutString(gpa, sub_file_path, .no_embedded_nulls),
4647 .owner = pkg,
4648 .stat = stat,
4649 .val = ptr_val,
4650 .src_loc = src_loc,
4651 };
4652 return ptr_val;
4653}
4654
4655pub fn scanNamespace(
4656 zcu: *Zcu,
4657 namespace_index: Namespace.Index,
4658 decls: []const Zir.Inst.Index,
4659 parent_decl: *Decl,
4660) Allocator.Error!void {
4661 const tracy = trace(@src());
4662 defer tracy.end();
4663
4664 const gpa = zcu.gpa;
4665 const namespace = zcu.namespacePtr(namespace_index);
4666
4667 // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather
4668 // than their name. We'll build an efficient mapping now, then discard the current `decls`.
4669 var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index) = .{};
4670 defer existing_by_inst.deinit(gpa);
4671
4672 try existing_by_inst.ensureTotalCapacity(gpa, @intCast(namespace.decls.count()));
4673
4674 for (namespace.decls.keys()) |decl_index| {
4675 const decl = zcu.declPtr(decl_index);
4676 existing_by_inst.putAssumeCapacityNoClobber(decl.zir_decl_index.unwrap().?, decl_index);
4677 }
4678
4679 var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
4680 defer seen_decls.deinit(gpa);
4681
4682 try zcu.comp.work_queue.ensureUnusedCapacity(decls.len);
4683
4684 namespace.decls.clearRetainingCapacity();
4685 try namespace.decls.ensureTotalCapacity(gpa, decls.len);
4686
4687 namespace.usingnamespace_set.clearRetainingCapacity();
4688
4689 var scan_decl_iter: ScanDeclIter = .{
4690 .zcu = zcu,
4691 .namespace_index = namespace_index,
4692 .parent_decl = parent_decl,
4693 .seen_decls = &seen_decls,
4694 .existing_by_inst = &existing_by_inst,
4695 .pass = .named,
4696 };
4697 for (decls) |decl_inst| {
4698 try scanDecl(&scan_decl_iter, decl_inst);
4699 }
4700 scan_decl_iter.pass = .unnamed;
4701 for (decls) |decl_inst| {
4702 try scanDecl(&scan_decl_iter, decl_inst);
4703 }
4704
4705 if (seen_decls.count() != namespace.decls.count()) {
4706 // Do a pass over the namespace contents and remove any decls from the last update
4707 // which were removed in this one.
4708 var i: usize = 0;
4709 while (i < namespace.decls.count()) {
4710 const decl_index = namespace.decls.keys()[i];
4711 const decl = zcu.declPtr(decl_index);
4712 if (!seen_decls.contains(decl.name)) {
4713 // We must preserve namespace ordering for @typeInfo.
4714 namespace.decls.orderedRemoveAt(i);
4715 i -= 1;
4716 }
4717 }
4718 }
4719}
4720
4721const ScanDeclIter = struct {
4722 zcu: *Zcu,
4723 namespace_index: Namespace.Index,
4724 parent_decl: *Decl,
4725 seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
4726 existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index),
4727 /// Decl scanning is run in two passes, so that we can detect when a generated
4728 /// name would clash with an explicit name and use a different one.
4729 pass: enum { named, unnamed },
4730 usingnamespace_index: usize = 0,
4731 comptime_index: usize = 0,
4732 unnamed_test_index: usize = 0,
4733
4734 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {
4735 const zcu = iter.zcu;
4736 const gpa = zcu.gpa;
4737 const ip = &zcu.intern_pool;
4738 var name = try ip.getOrPutStringFmt(gpa, fmt, args, .no_embedded_nulls);
4739 var gop = try iter.seen_decls.getOrPut(gpa, name);
4740 var next_suffix: u32 = 0;
4741 while (gop.found_existing) {
4742 name = try ip.getOrPutStringFmt(gpa, "{}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls);
4743 gop = try iter.seen_decls.getOrPut(gpa, name);
4744 next_suffix += 1;
4745 }
4746 return name;
4747 }
4748};
4749
4750fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void {
4751 const tracy = trace(@src());
4752 defer tracy.end();
4753
4754 const zcu = iter.zcu;
4755 const namespace_index = iter.namespace_index;
4756 const namespace = zcu.namespacePtr(namespace_index);
4757 const gpa = zcu.gpa;
4758 const zir = namespace.file_scope.zir;
4759 const ip = &zcu.intern_pool;
4760
4761 const inst_data = zir.instructions.items(.data)[@intFromEnum(decl_inst)].declaration;
4762 const extra = zir.extraData(Zir.Inst.Declaration, inst_data.payload_index);
4763 const declaration = extra.data;
4764
4765 const line = iter.parent_decl.src_line + declaration.line_offset;
4766
4767 // Every Decl needs a name.
4768 const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) {
4769 .@"comptime" => info: {
4770 if (iter.pass != .unnamed) return;
4771 const i = iter.comptime_index;
4772 iter.comptime_index += 1;
4773 break :info .{
4774 try iter.avoidNameConflict("comptime_{d}", .{i}),
4775 .@"comptime",
4776 false,
4777 };
4778 },
4779 .@"usingnamespace" => info: {
4780 // TODO: this isn't right! These should be considered unnamed. Name conflicts can happen here.
4781 // The problem is, we need to preserve the decl ordering for `@typeInfo`.
4782 // I'm not bothering to fix this now, since some upcoming changes will change this code significantly anyway.
4783 if (iter.pass != .named) return;
4784 const i = iter.usingnamespace_index;
4785 iter.usingnamespace_index += 1;
4786 break :info .{
4787 try iter.avoidNameConflict("usingnamespace_{d}", .{i}),
4788 .@"usingnamespace",
4789 false,
4790 };
4791 },
4792 .unnamed_test => info: {
4793 if (iter.pass != .unnamed) return;
4794 const i = iter.unnamed_test_index;
4795 iter.unnamed_test_index += 1;
4796 break :info .{
4797 try iter.avoidNameConflict("test_{d}", .{i}),
4798 .@"test",
4799 false,
4800 };
4801 },
4802 .decltest => info: {
4803 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
4804 if (iter.pass != .unnamed) return;
4805 assert(declaration.flags.has_doc_comment);
4806 const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end]));
4807 break :info .{
4808 try iter.avoidNameConflict("decltest.{s}", .{name}),
4809 .@"test",
4810 true,
4811 };
4812 },
4813 _ => if (declaration.name.isNamedTest(zir)) info: {
4814 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
4815 if (iter.pass != .unnamed) return;
4816 break :info .{
4817 try iter.avoidNameConflict("test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}),
4818 .@"test",
4819 true,
4820 };
4821 } else info: {
4822 if (iter.pass != .named) return;
4823 const name = try ip.getOrPutString(
4824 gpa,
4825 zir.nullTerminatedString(declaration.name.toString(zir).?),
4826 .no_embedded_nulls,
4827 );
4828 try iter.seen_decls.putNoClobber(gpa, name, {});
4829 break :info .{
4830 name,
4831 .named,
4832 false,
4833 };
4834 },
4835 };
4836
4837 switch (kind) {
4838 .@"usingnamespace" => try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1),
4839 .@"test" => try zcu.test_functions.ensureUnusedCapacity(gpa, 1),
4840 else => {},
4841 }
4842
4843 const tracked_inst = try ip.trackZir(gpa, iter.parent_decl.getFileScope(zcu), decl_inst);
4844
4845 // We create a Decl for it regardless of analysis status.
4846
4847 const prev_exported, const decl_index = if (iter.existing_by_inst.get(tracked_inst)) |decl_index| decl_index: {
4848 // We need only update this existing Decl.
4849 const decl = zcu.declPtr(decl_index);
4850 const was_exported = decl.is_exported;
4851 assert(decl.kind == kind); // ZIR tracking should preserve this
4852 decl.name = decl_name;
4853 decl.src_line = line;
4854 decl.is_pub = declaration.flags.is_pub;
4855 decl.is_exported = declaration.flags.is_export;
4856 break :decl_index .{ was_exported, decl_index };
4857 } else decl_index: {
4858 // Create and set up a new Decl.
4859 const new_decl_index = try zcu.allocateNewDecl(namespace_index);
4860 const new_decl = zcu.declPtr(new_decl_index);
4861 new_decl.kind = kind;
4862 new_decl.name = decl_name;
4863 new_decl.src_line = line;
4864 new_decl.is_pub = declaration.flags.is_pub;
4865 new_decl.is_exported = declaration.flags.is_export;
4866 new_decl.zir_decl_index = tracked_inst.toOptional();
4867 break :decl_index .{ false, new_decl_index };
4868 };
4869
4870 const decl = zcu.declPtr(decl_index);
4871
4872 namespace.decls.putAssumeCapacityNoClobberContext(decl_index, {}, .{ .zcu = zcu });
4873
4874 const comp = zcu.comp;
4875 const decl_mod = namespace.file_scope.mod;
4876 const want_analysis = declaration.flags.is_export or switch (kind) {
4877 .anon => unreachable,
4878 .@"comptime" => true,
4879 .@"usingnamespace" => a: {
4880 namespace.usingnamespace_set.putAssumeCapacityNoClobber(decl_index, declaration.flags.is_pub);
4881 break :a true;
4882 },
4883 .named => false,
4884 .@"test" => a: {
4885 if (!comp.config.is_test) break :a false;
4886 if (decl_mod != zcu.main_mod) break :a false;
4887 if (is_named_test and comp.test_filters.len > 0) {
4888 const decl_fqn = try namespace.fullyQualifiedName(zcu, decl_name);
4889 const decl_fqn_slice = decl_fqn.toSlice(ip);
4890 for (comp.test_filters) |test_filter| {
4891 if (mem.indexOf(u8, decl_fqn_slice, test_filter)) |_| break;
4892 } else break :a false;
4893 }
4894 zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update
4895 break :a true;
4896 },
4897 };
4898
4899 if (want_analysis) {
4900 // We will not queue analysis if the decl has been analyzed on a previous update and
4901 // `is_export` is unchanged. In this case, the incremental update mechanism will handle
4902 // re-analysis for us if necessary.
4903 if (prev_exported != declaration.flags.is_export or decl.analysis == .unreferenced) {
4904 log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{}' decl_index={d}", .{
4905 namespace.file_scope.sub_file_path, decl_name.fmt(ip), decl_index,
4906 });
4907 comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = decl_index });
4908 }
4909 }
4910
4911 if (decl.getOwnedFunction(zcu) != null) {
4912 // TODO this logic is insufficient; namespaces we don't re-scan may still require
4913 // updated line numbers. Look into this!
4914 // TODO Look into detecting when this would be unnecessary by storing enough state
4915 // in `Decl` to notice that the line number did not change.
4916 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
4917 }
4918}
4919
4920/// Cancel the creation of an anon decl and delete any references to it.
4921/// If other decls depend on this decl, they must be aborted first.
4922pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
4923 assert(!mod.declIsRoot(decl_index));
4924 mod.destroyDecl(decl_index);
4925}
4926
4927/// Finalize the creation of an anon decl.
4928pub fn finalizeAnonDecl(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
4929 if (mod.declPtr(decl_index).typeOf(mod).isFnOrHasRuntimeBits(mod)) {
4930 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
4931 }
4932}
4933
4934/// Delete all the Export objects that are caused by this Decl. Re-analysis of
4935/// this Decl will cause them to be re-created (or not).
4936fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
4937 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
4938
4939 for (export_owners.items) |exp| {
4940 switch (exp.exported) {
4941 .decl_index => |exported_decl_index| {
4942 if (mod.decl_exports.getPtr(exported_decl_index)) |export_list| {
4943 // Remove exports with owner_decl matching the regenerating decl.
4944 const list = export_list.items;
4945 var i: usize = 0;
4946 var new_len = list.len;
4947 while (i < new_len) {
4948 if (list[i].owner_decl == decl_index) {
4949 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
4950 new_len -= 1;
4951 } else {
4952 i += 1;
4953 }
4954 }
4955 export_list.shrinkAndFree(mod.gpa, new_len);
4956 if (new_len == 0) {
4957 assert(mod.decl_exports.swapRemove(exported_decl_index));
4958 }
4959 }
4960 },
4961 .value => |value| {
4962 if (mod.value_exports.getPtr(value)) |export_list| {
4963 // Remove exports with owner_decl matching the regenerating decl.
4964 const list = export_list.items;
4965 var i: usize = 0;
4966 var new_len = list.len;
4967 while (i < new_len) {
4968 if (list[i].owner_decl == decl_index) {
4969 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
4970 new_len -= 1;
4971 } else {
4972 i += 1;
4973 }
4974 }
4975 export_list.shrinkAndFree(mod.gpa, new_len);
4976 if (new_len == 0) {
4977 assert(mod.value_exports.swapRemove(value));
4978 }
4979 }
4980 },
4981 }
4982 if (mod.comp.bin_file) |lf| {
4983 try lf.deleteDeclExport(decl_index, exp.opts.name);
4984 }
4985 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
4986 failed_kv.value.destroy(mod.gpa);
4987 }
4988 mod.gpa.destroy(exp);
4989 }
4990 export_owners.deinit(mod.gpa);
4991}
4992
4993pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocator) SemaError!Air {
4994 const tracy = trace(@src());
4995 defer tracy.end();
4996
4997 const gpa = mod.gpa;
4998 const ip = &mod.intern_pool;
4999 const func = mod.funcInfo(func_index);
5000 const decl_index = func.owner_decl;
5001 const decl = mod.declPtr(decl_index);
5002
5003 log.debug("func name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)});
5004 defer blk: {
5005 log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});
5006 }
5007
5008 const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
5009 defer decl_prog_node.end();
5010
5011 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
5012
5013 var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
5014 defer comptime_err_ret_trace.deinit();
5015
5016 // In the case of a generic function instance, this is the type of the
5017 // instance, which has comptime parameters elided. In other words, it is
5018 // the runtime-known parameters only, not to be confused with the
5019 // generic_owner function type, which potentially has more parameters,
5020 // including comptime parameters.
5021 const fn_ty = decl.typeOf(mod);
5022 const fn_ty_info = mod.typeToFunc(fn_ty).?;
5023
5024 var sema: Sema = .{
5025 .mod = mod,
5026 .gpa = gpa,
5027 .arena = arena,
5028 .code = decl.getFileScope(mod).zir,
5029 .owner_decl = decl,
5030 .owner_decl_index = decl_index,
5031 .func_index = func_index,
5032 .func_is_naked = fn_ty_info.cc == .Naked,
5033 .fn_ret_ty = Type.fromInterned(fn_ty_info.return_type),
5034 .fn_ret_ty_ies = null,
5035 .owner_func_index = func_index,
5036 .branch_quota = @max(func.branchQuota(ip).*, Sema.default_branch_quota),
5037 .comptime_err_ret_trace = &comptime_err_ret_trace,
5038 };
5039 defer sema.deinit();
5040
5041 // Every runtime function has a dependency on the source of the Decl it originates from.
5042 // It also depends on the value of its owner Decl.
5043 try sema.declareDependency(.{ .src_hash = decl.zir_decl_index.unwrap().? });
5044 try sema.declareDependency(.{ .decl_val = decl_index });
5045
5046 if (func.analysis(ip).inferred_error_set) {
5047 const ies = try arena.create(Sema.InferredErrorSet);
5048 ies.* = .{ .func = func_index };
5049 sema.fn_ret_ty_ies = ies;
5050 }
5051
5052 // reset in case calls to errorable functions are removed.
5053 func.analysis(ip).calls_or_awaits_errorable_fn = false;
5054
5055 // First few indexes of extra are reserved and set at the end.
5056 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
5057 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
5058 sema.air_extra.items.len += reserved_count;
5059
5060 var inner_block: Sema.Block = .{
5061 .parent = null,
5062 .sema = &sema,
5063 .namespace = decl.src_namespace,
5064 .instructions = .{},
5065 .inlining = null,
5066 .is_comptime = false,
5067 .src_base_inst = inst: {
5068 const owner_info = if (func.generic_owner == .none)
5069 func
5070 else
5071 mod.funcInfo(func.generic_owner);
5072 const orig_decl = mod.declPtr(owner_info.owner_decl);
5073 break :inst orig_decl.zir_decl_index.unwrap().?;
5074 },
5075 .type_name_ctx = decl.name,
5076 };
5077 defer inner_block.instructions.deinit(gpa);
5078
5079 const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).resolve(ip));
5080
5081 // Here we are performing "runtime semantic analysis" for a function body, which means
5082 // we must map the parameter ZIR instructions to `arg` AIR instructions.
5083 // AIR requires the `arg` parameters to be the first N instructions.
5084 // This could be a generic function instantiation, however, in which case we need to
5085 // map the comptime parameters to constant values and only emit arg AIR instructions
5086 // for the runtime ones.
5087 const runtime_params_len = fn_ty_info.param_types.len;
5088 try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len);
5089 try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len);
5090 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
5091
5092 // In the case of a generic function instance, pre-populate all the comptime args.
5093 if (func.comptime_args.len != 0) {
5094 for (
5095 fn_info.param_body[0..func.comptime_args.len],
5096 func.comptime_args.get(ip),
5097 ) |inst, comptime_arg| {
5098 if (comptime_arg == .none) continue;
5099 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.internedToRef(comptime_arg));
5100 }
5101 }
5102
5103 const src_params_len = if (func.comptime_args.len != 0)
5104 func.comptime_args.len
5105 else
5106 runtime_params_len;
5107
5108 var runtime_param_index: usize = 0;
5109 for (fn_info.param_body[0..src_params_len], 0..) |inst, src_param_index| {
5110 const gop = sema.inst_map.getOrPutAssumeCapacity(inst);
5111 if (gop.found_existing) continue; // provided above by comptime arg
5112
5113 const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
5114 runtime_param_index += 1;
5115
5116 const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) {
5117 error.GenericPoison => unreachable,
5118 error.ComptimeReturn => unreachable,
5119 error.ComptimeBreak => unreachable,
5120 else => |e| return e,
5121 };
5122 if (opt_opv) |opv| {
5123 gop.value_ptr.* = Air.internedToRef(opv.toIntern());
5124 continue;
5125 }
5126 const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
5127 gop.value_ptr.* = arg_index.toRef();
5128 inner_block.instructions.appendAssumeCapacity(arg_index);
5129 sema.air_instructions.appendAssumeCapacity(.{
5130 .tag = .arg,
5131 .data = .{ .arg = .{
5132 .ty = Air.internedToRef(param_ty),
5133 .src_index = @intCast(src_param_index),
5134 } },
5135 });
5136 }
5137
5138 func.analysis(ip).state = .in_progress;
5139
5140 const last_arg_index = inner_block.instructions.items.len;
5141
5142 // Save the error trace as our first action in the function.
5143 // If this is unnecessary after all, Liveness will clean it up for us.
5144 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&inner_block);
5145 sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
5146 inner_block.error_return_trace_index = error_return_trace_index;
5147
5148 sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) {
5149 // TODO make these unreachable instead of @panic
5150 error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
5151 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
5152 else => |e| return e,
5153 };
5154
5155 for (sema.unresolved_inferred_allocs.keys()) |ptr_inst| {
5156 // The lack of a resolve_inferred_alloc means that this instruction
5157 // is unused so it just has to be a no-op.
5158 sema.air_instructions.set(@intFromEnum(ptr_inst), .{
5159 .tag = .alloc,
5160 .data = .{ .ty = Type.single_const_pointer_to_comptime_int },
5161 });
5162 }
5163
5164 // If we don't get an error return trace from a caller, create our own.
5165 if (func.analysis(ip).calls_or_awaits_errorable_fn and
5166 mod.comp.config.any_error_tracing and
5167 !sema.fn_ret_ty.isError(mod))
5168 {
5169 sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) {
5170 // TODO make these unreachable instead of @panic
5171 error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
5172 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
5173 error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"),
5174 else => |e| return e,
5175 };
5176 }
5177
5178 // Copy the block into place and mark that as the main block.
5179 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
5180 inner_block.instructions.items.len);
5181 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
5182 .body_len = @intCast(inner_block.instructions.items.len),
5183 });
5184 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(inner_block.instructions.items));
5185 sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;
5186
5187 // Resolving inferred error sets is done *before* setting the function
5188 // state to success, so that "unable to resolve inferred error set" errors
5189 // can be emitted here.
5190 if (sema.fn_ret_ty_ies) |ies| {
5191 sema.resolveInferredErrorSetPtr(&inner_block, .{
5192 .base_node_inst = inner_block.src_base_inst,
5193 .offset = LazySrcLoc.Offset.nodeOffset(0),
5194 }, ies) catch |err| switch (err) {
5195 error.GenericPoison => unreachable,
5196 error.ComptimeReturn => unreachable,
5197 error.ComptimeBreak => unreachable,
5198 error.AnalysisFail => {
5199 // In this case our function depends on a type that had a compile error.
5200 // We should not try to lower this function.
5201 decl.analysis = .dependency_failure;
5202 return error.AnalysisFail;
5203 },
5204 else => |e| return e,
5205 };
5206 assert(ies.resolved != .none);
5207 ip.funcIesResolved(func_index).* = ies.resolved;
5208 }
5209
5210 func.analysis(ip).state = .success;
5211
5212 // Finally we must resolve the return type and parameter types so that backends
5213 // have full access to type information.
5214 // Crucially, this happens *after* we set the function state to success above,
5215 // so that dependencies on the function body will now be satisfied rather than
5216 // result in circular dependency errors.
5217 sema.resolveFnTypes(fn_ty) catch |err| switch (err) {
5218 error.GenericPoison => unreachable,
5219 error.ComptimeReturn => unreachable,
5220 error.ComptimeBreak => unreachable,
5221 error.AnalysisFail => {
5222 // In this case our function depends on a type that had a compile error.
5223 // We should not try to lower this function.
5224 decl.analysis = .dependency_failure;
5225 return error.AnalysisFail;
5226 },
5227 else => |e| return e,
5228 };
5229
5230 // Similarly, resolve any queued up types that were requested to be resolved for
5231 // the backends.
5232 for (sema.types_to_resolve.keys()) |ty| {
5233 sema.resolveTypeFully(Type.fromInterned(ty)) catch |err| switch (err) {
5234 error.GenericPoison => unreachable,
5235 error.ComptimeReturn => unreachable,
5236 error.ComptimeBreak => unreachable,
5237 error.AnalysisFail => {
5238 // In this case our function depends on a type that had a compile error.
5239 // We should not try to lower this function.
5240 decl.analysis = .dependency_failure;
5241 return error.AnalysisFail;
5242 },
5243 else => |e| return e,
5244 };
5245 }
5246
5247 return .{
5248 .instructions = sema.air_instructions.toOwnedSlice(),
5249 .extra = try sema.air_extra.toOwnedSlice(gpa),
5250 };
5251}
5252
5253pub fn createNamespace(mod: *Module, initialization: Namespace) !Namespace.Index {
5254 return mod.intern_pool.createNamespace(mod.gpa, initialization);
5255}
5256
5257pub fn destroyNamespace(mod: *Module, index: Namespace.Index) void {
5258 return mod.intern_pool.destroyNamespace(mod.gpa, index);
5259}
5260
5261pub fn allocateNewDecl(zcu: *Zcu, namespace: Namespace.Index) !Decl.Index {
5262 const gpa = zcu.gpa;
5263 const decl_index = try zcu.intern_pool.createDecl(gpa, .{
5264 .name = undefined,
5265 .src_namespace = namespace,
5266 .src_line = undefined,
5267 .has_tv = false,
5268 .owns_tv = false,
5269 .val = undefined,
5270 .alignment = undefined,
5271 .@"linksection" = .none,
5272 .@"addrspace" = .generic,
5273 .analysis = .unreferenced,
5274 .zir_decl_index = .none,
5275 .is_pub = false,
5276 .is_exported = false,
5277 .kind = .anon,
5278 });
5279
5280 if (zcu.emit_h) |zcu_emit_h| {
5281 if (@intFromEnum(decl_index) >= zcu_emit_h.allocated_emit_h.len) {
5282 try zcu_emit_h.allocated_emit_h.append(gpa, .{});
5283 assert(@intFromEnum(decl_index) == zcu_emit_h.allocated_emit_h.len);
5284 }
5285 }
5286
5287 return decl_index;
5288}
5289
5290pub fn getErrorValue(
5291 mod: *Module,
5292 name: InternPool.NullTerminatedString,
5293) Allocator.Error!ErrorInt {
5294 const gop = try mod.global_error_set.getOrPut(mod.gpa, name);
5295 return @as(ErrorInt, @intCast(gop.index));
5296}
5297
5298pub fn getErrorValueFromSlice(
5299 mod: *Module,
5300 name: []const u8,
5301) Allocator.Error!ErrorInt {
5302 const interned_name = try mod.intern_pool.getOrPutString(mod.gpa, name);
5303 return getErrorValue(mod, interned_name);
5304}
5305
5306pub fn errorSetBits(mod: *Module) u16 {
5307 if (mod.error_limit == 0) return 0;
5308 return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error
5309}
5310
5311pub fn initNewAnonDecl(
5312 mod: *Module,
5313 new_decl_index: Decl.Index,
5314 src_line: u32,
5315 val: Value,
5316 name: InternPool.NullTerminatedString,
5317) Allocator.Error!void {
5318 const new_decl = mod.declPtr(new_decl_index);
5319
5320 new_decl.name = name;
5321 new_decl.src_line = src_line;
5322 new_decl.val = val;
5323 new_decl.alignment = .none;
5324 new_decl.@"linksection" = .none;
5325 new_decl.has_tv = true;
5326 new_decl.analysis = .complete;
5327}
5328
5329pub fn errNoteNonLazy(
5330 mod: *Module,
5331 src_loc: SrcLoc,
5332 parent: *ErrorMsg,
5333 comptime format: []const u8,
5334 args: anytype,
5335) error{OutOfMemory}!void {
5336 if (src_loc.lazy == .unneeded) {
5337 assert(parent.src_loc.lazy == .unneeded);
5338 return;
5339 }
5340 const msg = try std.fmt.allocPrint(mod.gpa, format, args);
5341 errdefer mod.gpa.free(msg);
5342
5343 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
5344 parent.notes[parent.notes.len - 1] = .{
5345 .src_loc = src_loc,
5346 .msg = msg,
5347 };
5348}
5349
5350/// Deprecated. There is no global target for a Zig Compilation Unit. Instead,
5351/// look up the target based on the Module that contains the source code being
5352/// analyzed.
5353pub fn getTarget(zcu: Module) Target {
5354 return zcu.root_mod.resolved_target.result;
5355}
5356
5357/// Deprecated. There is no global optimization mode for a Zig Compilation
5358/// Unit. Instead, look up the optimization mode based on the Module that
5359/// contains the source code being analyzed.
5360pub fn optimizeMode(zcu: Module) std.builtin.OptimizeMode {
5361 return zcu.root_mod.optimize_mode;
5362}
5363
5364fn lockAndClearFileCompileError(mod: *Module, file: *File) void {
5365 switch (file.status) {
5366 .success_zir, .retryable_failure => {},
5367 .never_loaded, .parse_failure, .astgen_failure => {
5368 mod.comp.mutex.lock();
5369 defer mod.comp.mutex.unlock();
5370 if (mod.failed_files.fetchSwapRemove(file)) |kv| {
5371 if (kv.value) |msg| msg.destroy(mod.gpa); // Delete previous error message.
5372 }
5373 },
5374 }
5375}
5376
5377/// Called from `Compilation.update`, after everything is done, just before
5378/// reporting compile errors. In this function we emit exported symbol collision
5379/// errors and communicate exported symbols to the linker backend.
5380pub fn processExports(mod: *Module) !void {
5381 // Map symbol names to `Export` for name collision detection.
5382 var symbol_exports: SymbolExports = .{};
5383 defer symbol_exports.deinit(mod.gpa);
5384
5385 for (mod.decl_exports.keys(), mod.decl_exports.values()) |exported_decl, exports_list| {
5386 const exported: Exported = .{ .decl_index = exported_decl };
5387 try processExportsInner(mod, &symbol_exports, exported, exports_list.items);
5388 }
5389
5390 for (mod.value_exports.keys(), mod.value_exports.values()) |exported_value, exports_list| {
5391 const exported: Exported = .{ .value = exported_value };
5392 try processExportsInner(mod, &symbol_exports, exported, exports_list.items);
5393 }
5394}
5395
5396const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export);
5397
5398fn processExportsInner(
5399 zcu: *Zcu,
5400 symbol_exports: *SymbolExports,
5401 exported: Exported,
5402 exports: []const *Export,
5403) error{OutOfMemory}!void {
5404 const gpa = zcu.gpa;
5405
5406 for (exports) |new_export| {
5407 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
5408 if (gop.found_existing) {
5409 new_export.status = .failed_retryable;
5410 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
5411 const src_loc = new_export.getSrcLoc(zcu);
5412 const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{
5413 new_export.opts.name.fmt(&zcu.intern_pool),
5414 });
5415 errdefer msg.destroy(gpa);
5416 const other_export = gop.value_ptr.*;
5417 const other_src_loc = other_export.getSrcLoc(zcu);
5418 try zcu.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{});
5419 zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5420 new_export.status = .failed;
5421 } else {
5422 gop.value_ptr.* = new_export;
5423 }
5424 }
5425 if (zcu.comp.bin_file) |lf| {
5426 try handleUpdateExports(zcu, exports, lf.updateExports(zcu, exported, exports));
5427 } else if (zcu.llvm_object) |llvm_object| {
5428 if (build_options.only_c) unreachable;
5429 try handleUpdateExports(zcu, exports, llvm_object.updateExports(zcu, exported, exports));
5430 }
5431}
5432
5433fn handleUpdateExports(
5434 zcu: *Zcu,
5435 exports: []const *Export,
5436 result: link.File.UpdateExportsError!void,
5437) Allocator.Error!void {
5438 const gpa = zcu.gpa;
5439 result catch |err| switch (err) {
5440 error.OutOfMemory => return error.OutOfMemory,
5441 error.AnalysisFail => {
5442 const new_export = exports[0];
5443 new_export.status = .failed_retryable;
5444 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
5445 const src_loc = new_export.getSrcLoc(zcu);
5446 const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{
5447 @errorName(err),
5448 });
5449 zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5450 },
5451 };
5452}
5453
5454pub fn populateTestFunctions(
5455 mod: *Module,
5456 main_progress_node: std.Progress.Node,
5457) !void {
5458 const gpa = mod.gpa;
5459 const ip = &mod.intern_pool;
5460 const builtin_mod = mod.root_mod.getBuiltinDependency();
5461 const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file;
5462 const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);
5463 const builtin_namespace = mod.namespacePtr(root_decl.src_namespace);
5464 const test_functions_str = try ip.getOrPutString(gpa, "test_functions", .no_embedded_nulls);
5465 const decl_index = builtin_namespace.decls.getKeyAdapted(
5466 test_functions_str,
5467 DeclAdapter{ .zcu = mod },
5468 ).?;
5469 {
5470 // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions`
5471 // was not referenced by start code.
5472 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
5473 defer {
5474 mod.sema_prog_node.end();
5475 mod.sema_prog_node = undefined;
5476 }
5477 try mod.ensureDeclAnalyzed(decl_index);
5478 }
5479
5480 const decl = mod.declPtr(decl_index);
5481 const test_fn_ty = decl.typeOf(mod).slicePtrFieldType(mod).childType(mod);
5482
5483 const array_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = array: {
5484 // Add mod.test_functions to an array decl then make the test_functions
5485 // decl reference it as a slice.
5486 const test_fn_vals = try gpa.alloc(InternPool.Index, mod.test_functions.count());
5487 defer gpa.free(test_fn_vals);
5488
5489 for (test_fn_vals, mod.test_functions.keys()) |*test_fn_val, test_decl_index| {
5490 const test_decl = mod.declPtr(test_decl_index);
5491 const test_decl_name = try test_decl.fullyQualifiedName(mod);
5492 const test_decl_name_len = test_decl_name.length(ip);
5493 const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = n: {
5494 const test_name_ty = try mod.arrayType(.{
5495 .len = test_decl_name_len,
5496 .child = .u8_type,
5497 });
5498 const test_name_val = try mod.intern(.{ .aggregate = .{
5499 .ty = test_name_ty.toIntern(),
5500 .storage = .{ .bytes = test_decl_name.toString() },
5501 } });
5502 break :n .{
5503 .orig_ty = (try mod.singleConstPtrType(test_name_ty)).toIntern(),
5504 .val = test_name_val,
5505 };
5506 };
5507
5508 const test_fn_fields = .{
5509 // name
5510 try mod.intern(.{ .slice = .{
5511 .ty = .slice_const_u8_type,
5512 .ptr = try mod.intern(.{ .ptr = .{
5513 .ty = .manyptr_const_u8_type,
5514 .base_addr = .{ .anon_decl = test_name_anon_decl },
5515 .byte_offset = 0,
5516 } }),
5517 .len = try mod.intern(.{ .int = .{
5518 .ty = .usize_type,
5519 .storage = .{ .u64 = test_decl_name_len },
5520 } }),
5521 } }),
5522 // func
5523 try mod.intern(.{ .ptr = .{
5524 .ty = try mod.intern(.{ .ptr_type = .{
5525 .child = test_decl.typeOf(mod).toIntern(),
5526 .flags = .{
5527 .is_const = true,
5528 },
5529 } }),
5530 .base_addr = .{ .decl = test_decl_index },
5531 .byte_offset = 0,
5532 } }),
5533 };
5534 test_fn_val.* = try mod.intern(.{ .aggregate = .{
5535 .ty = test_fn_ty.toIntern(),
5536 .storage = .{ .elems = &test_fn_fields },
5537 } });
5538 }
5539
5540 const array_ty = try mod.arrayType(.{
5541 .len = test_fn_vals.len,
5542 .child = test_fn_ty.toIntern(),
5543 .sentinel = .none,
5544 });
5545 const array_val = try mod.intern(.{ .aggregate = .{
5546 .ty = array_ty.toIntern(),
5547 .storage = .{ .elems = test_fn_vals },
5548 } });
5549 break :array .{
5550 .orig_ty = (try mod.singleConstPtrType(array_ty)).toIntern(),
5551 .val = array_val,
5552 };
5553 };
5554
5555 {
5556 const new_ty = try mod.ptrType(.{
5557 .child = test_fn_ty.toIntern(),
5558 .flags = .{
5559 .is_const = true,
5560 .size = .Slice,
5561 },
5562 });
5563 const new_val = decl.val;
5564 const new_init = try mod.intern(.{ .slice = .{
5565 .ty = new_ty.toIntern(),
5566 .ptr = try mod.intern(.{ .ptr = .{
5567 .ty = new_ty.slicePtrFieldType(mod).toIntern(),
5568 .base_addr = .{ .anon_decl = array_anon_decl },
5569 .byte_offset = 0,
5570 } }),
5571 .len = (try mod.intValue(Type.usize, mod.test_functions.count())).toIntern(),
5572 } });
5573 ip.mutateVarInit(decl.val.toIntern(), new_init);
5574
5575 // Since we are replacing the Decl's value we must perform cleanup on the
5576 // previous value.
5577 decl.val = new_val;
5578 decl.has_tv = true;
5579 }
5580 {
5581 mod.codegen_prog_node = main_progress_node.start("Code Generation", 0);
5582 defer {
5583 mod.codegen_prog_node.end();
5584 mod.codegen_prog_node = undefined;
5585 }
5586
5587 try mod.linkerUpdateDecl(decl_index);
5588 }
5589}
5590
5591pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {
5592 const comp = zcu.comp;
5593
5594 const decl = zcu.declPtr(decl_index);
5595
5596 const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(&zcu.intern_pool), 0);
5597 defer codegen_prog_node.end();
5598
5599 if (comp.bin_file) |lf| {
5600 lf.updateDecl(zcu, decl_index) catch |err| switch (err) {
5601 error.OutOfMemory => return error.OutOfMemory,
5602 error.AnalysisFail => {
5603 decl.analysis = .codegen_failure;
5604 },
5605 else => {
5606 const gpa = zcu.gpa;
5607 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
5608 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
5609 gpa,
5610 decl.navSrcLoc(zcu).upgrade(zcu),
5611 "unable to codegen: {s}",
5612 .{@errorName(err)},
5613 ));
5614 decl.analysis = .codegen_failure;
5615 try zcu.retryable_failures.append(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
5616 },
5617 };
5618 } else if (zcu.llvm_object) |llvm_object| {
5619 if (build_options.only_c) unreachable;
5620 llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) {
5621 error.OutOfMemory => return error.OutOfMemory,
5622 error.AnalysisFail => {
5623 decl.analysis = .codegen_failure;
5624 },
5625 };
5626 }
5627}
5628
5629fn reportRetryableFileError(
5630 mod: *Module,
5631 file: *File,
5632 comptime format: []const u8,
5633 args: anytype,
5634) error{OutOfMemory}!void {
5635 file.status = .retryable_failure;
5636
5637 const err_msg = try ErrorMsg.create(
5638 mod.gpa,
5639 .{
5640 .file_scope = file,
5641 .base_node = 0,
5642 .lazy = .entire_file,
5643 },
5644 format,
5645 args,
5646 );
5647 errdefer err_msg.destroy(mod.gpa);
5648
5649 mod.comp.mutex.lock();
5650 defer mod.comp.mutex.unlock();
5651
5652 const gop = try mod.failed_files.getOrPut(mod.gpa, file);
5653 if (gop.found_existing) {
5654 if (gop.value_ptr.*) |old_err_msg| {
5655 old_err_msg.destroy(mod.gpa);
5656 }
5657 }
5658 gop.value_ptr.* = err_msg;
5659}
5660
5661pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void {
5662 const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index);
5663 if (gop.found_existing) {
5664 const new_value = try std.fmt.allocPrint(mod.gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
5665 mod.gpa.free(gop.value_ptr.*);
5666 gop.value_ptr.* = new_value;
5667 } else {
5668 gop.value_ptr.* = try mod.gpa.dupe(u8, source);
5669 }
5670}
5671
5672pub fn getDeclExports(mod: Module, decl_index: Decl.Index) []const *Export {
5673 if (mod.decl_exports.get(decl_index)) |l| {
5674 return l.items;
5675 } else {
5676 return &[0]*Export{};
5677 }
5678}
5679
5680pub const Feature = enum {
5681 panic_fn,
5682 panic_unwrap_error,
5683 safety_check_formatted,
5684 error_return_trace,
5685 is_named_enum_value,
5686 error_set_has_value,
5687 field_reordering,
5688 /// When this feature is supported, the backend supports the following AIR instructions:
5689 /// * `Air.Inst.Tag.add_safe`
5690 /// * `Air.Inst.Tag.sub_safe`
5691 /// * `Air.Inst.Tag.mul_safe`
5692 /// The motivation for this feature is that it makes AIR smaller, and makes it easier
5693 /// to generate better machine code in the backends. All backends should migrate to
5694 /// enabling this feature.
5695 safety_checked_instructions,
5696};
5697
5698pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool {
5699 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;
5700 const ofmt = zcu.root_mod.resolved_target.result.ofmt;
5701 const use_llvm = zcu.comp.config.use_llvm;
5702 return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature);
5703}
5704
5705/// Shortcut for calling `intern_pool.get`.
5706pub fn intern(mod: *Module, key: InternPool.Key) Allocator.Error!InternPool.Index {
5707 return mod.intern_pool.get(mod.gpa, key);
5708}
5709
5710/// Shortcut for calling `intern_pool.getCoerced`.
5711pub fn getCoerced(mod: *Module, val: Value, new_ty: Type) Allocator.Error!Value {
5712 return Value.fromInterned((try mod.intern_pool.getCoerced(mod.gpa, val.toIntern(), new_ty.toIntern())));
5713}
5714
5715pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type {
5716 return Type.fromInterned((try intern(mod, .{ .int_type = .{
5717 .signedness = signedness,
5718 .bits = bits,
5719 } })));
5720}
5721
5722pub fn errorIntType(mod: *Module) std.mem.Allocator.Error!Type {
5723 return mod.intType(.unsigned, mod.errorSetBits());
5724}
5725
5726pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type {
5727 const i = try intern(mod, .{ .array_type = info });
5728 return Type.fromInterned(i);
5729}
5730
5731pub fn vectorType(mod: *Module, info: InternPool.Key.VectorType) Allocator.Error!Type {
5732 const i = try intern(mod, .{ .vector_type = info });
5733 return Type.fromInterned(i);
5734}
5735
5736pub fn optionalType(mod: *Module, child_type: InternPool.Index) Allocator.Error!Type {
5737 const i = try intern(mod, .{ .opt_type = child_type });
5738 return Type.fromInterned(i);
5739}
5740
5741pub fn ptrType(mod: *Module, info: InternPool.Key.PtrType) Allocator.Error!Type {
5742 var canon_info = info;
5743
5744 if (info.flags.size == .C) canon_info.flags.is_allowzero = true;
5745
5746 // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee
5747 // type, we change it to 0 here. If this causes an assertion trip because the
5748 // pointee type needs to be resolved more, that needs to be done before calling
5749 // this ptr() function.
5750 if (info.flags.alignment != .none and
5751 info.flags.alignment == Type.fromInterned(info.child).abiAlignment(mod))
5752 {
5753 canon_info.flags.alignment = .none;
5754 }
5755
5756 switch (info.flags.vector_index) {
5757 // Canonicalize host_size. If it matches the bit size of the pointee type,
5758 // we change it to 0 here. If this causes an assertion trip, the pointee type
5759 // needs to be resolved before calling this ptr() function.
5760 .none => if (info.packed_offset.host_size != 0) {
5761 const elem_bit_size = Type.fromInterned(info.child).bitSize(mod);
5762 assert(info.packed_offset.bit_offset + elem_bit_size <= info.packed_offset.host_size * 8);
5763 if (info.packed_offset.host_size * 8 == elem_bit_size) {
5764 canon_info.packed_offset.host_size = 0;
5765 }
5766 },
5767 .runtime => {},
5768 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
5769 }
5770
5771 return Type.fromInterned((try intern(mod, .{ .ptr_type = canon_info })));
5772}
5773
5774pub fn singleMutPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
5775 return ptrType(mod, .{ .child = child_type.toIntern() });
5776}
5777
5778pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
5779 return ptrType(mod, .{
5780 .child = child_type.toIntern(),
5781 .flags = .{
5782 .is_const = true,
5783 },
5784 });
5785}
5786
5787pub fn manyConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
5788 return ptrType(mod, .{
5789 .child = child_type.toIntern(),
5790 .flags = .{
5791 .size = .Many,
5792 .is_const = true,
5793 },
5794 });
5795}
5796
5797pub fn adjustPtrTypeChild(mod: *Module, ptr_ty: Type, new_child: Type) Allocator.Error!Type {
5798 var info = ptr_ty.ptrInfo(mod);
5799 info.child = new_child.toIntern();
5800 return mod.ptrType(info);
5801}
5802
5803pub fn funcType(mod: *Module, key: InternPool.GetFuncTypeKey) Allocator.Error!Type {
5804 return Type.fromInterned((try mod.intern_pool.getFuncType(mod.gpa, key)));
5805}
5806
5807/// Use this for `anyframe->T` only.
5808/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
5809pub fn anyframeType(mod: *Module, payload_ty: Type) Allocator.Error!Type {
5810 return Type.fromInterned((try intern(mod, .{ .anyframe_type = payload_ty.toIntern() })));
5811}
5812
5813pub fn errorUnionType(mod: *Module, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {
5814 return Type.fromInterned((try intern(mod, .{ .error_union_type = .{
5815 .error_set_type = error_set_ty.toIntern(),
5816 .payload_type = payload_ty.toIntern(),
5817 } })));
5818}
5819
5820pub fn singleErrorSetType(mod: *Module, name: InternPool.NullTerminatedString) Allocator.Error!Type {
5821 const names: *const [1]InternPool.NullTerminatedString = &name;
5822 const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names);
5823 return Type.fromInterned(new_ty);
5824}
5825
5826/// Sorts `names` in place.
5827pub fn errorSetFromUnsortedNames(
5828 mod: *Module,
5829 names: []InternPool.NullTerminatedString,
5830) Allocator.Error!Type {
5831 std.mem.sort(
5832 InternPool.NullTerminatedString,
5833 names,
5834 {},
5835 InternPool.NullTerminatedString.indexLessThan,
5836 );
5837 const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names);
5838 return Type.fromInterned(new_ty);
5839}
5840
5841/// Supports only pointers, not pointer-like optionals.
5842pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
5843 assert(ty.zigTypeTag(mod) == .Pointer and !ty.isSlice(mod));
5844 assert(x != 0 or ty.isAllowzeroPtr(mod));
5845 const i = try intern(mod, .{ .ptr = .{
5846 .ty = ty.toIntern(),
5847 .base_addr = .int,
5848 .byte_offset = x,
5849 } });
5850 return Value.fromInterned(i);
5851}
5852
5853/// Creates an enum tag value based on the integer tag value.
5854pub fn enumValue(mod: *Module, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value {
5855 if (std.debug.runtime_safety) {
5856 const tag = ty.zigTypeTag(mod);
5857 assert(tag == .Enum);
5858 }
5859 const i = try intern(mod, .{ .enum_tag = .{
5860 .ty = ty.toIntern(),
5861 .int = tag_int,
5862 } });
5863 return Value.fromInterned(i);
5864}
5865
5866/// Creates an enum tag value based on the field index according to source code
5867/// declaration order.
5868pub fn enumValueFieldIndex(mod: *Module, ty: Type, field_index: u32) Allocator.Error!Value {
5869 const ip = &mod.intern_pool;
5870 const gpa = mod.gpa;
5871 const enum_type = ip.loadEnumType(ty.toIntern());
5872
5873 if (enum_type.values.len == 0) {
5874 // Auto-numbered fields.
5875 return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{
5876 .ty = ty.toIntern(),
5877 .int = try ip.get(gpa, .{ .int = .{
5878 .ty = enum_type.tag_ty,
5879 .storage = .{ .u64 = field_index },
5880 } }),
5881 } })));
5882 }
5883
5884 return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{
5885 .ty = ty.toIntern(),
5886 .int = enum_type.values.get(ip)[field_index],
5887 } })));
5888}
5889
5890pub fn undefValue(mod: *Module, ty: Type) Allocator.Error!Value {
5891 return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
5892}
5893
5894pub fn undefRef(mod: *Module, ty: Type) Allocator.Error!Air.Inst.Ref {
5895 return Air.internedToRef((try mod.undefValue(ty)).toIntern());
5896}
5897
5898pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
5899 if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
5900 if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
5901 var limbs_buffer: [4]usize = undefined;
5902 var big_int = BigIntMutable.init(&limbs_buffer, x);
5903 return intValue_big(mod, ty, big_int.toConst());
5904}
5905
5906pub fn intRef(mod: *Module, ty: Type, x: anytype) Allocator.Error!Air.Inst.Ref {
5907 return Air.internedToRef((try mod.intValue(ty, x)).toIntern());
5908}
5909
5910pub fn intValue_big(mod: *Module, ty: Type, x: BigIntConst) Allocator.Error!Value {
5911 const i = try intern(mod, .{ .int = .{
5912 .ty = ty.toIntern(),
5913 .storage = .{ .big_int = x },
5914 } });
5915 return Value.fromInterned(i);
5916}
5917
5918pub fn intValue_u64(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
5919 const i = try intern(mod, .{ .int = .{
5920 .ty = ty.toIntern(),
5921 .storage = .{ .u64 = x },
5922 } });
5923 return Value.fromInterned(i);
5924}
5925
5926pub fn intValue_i64(mod: *Module, ty: Type, x: i64) Allocator.Error!Value {
5927 const i = try intern(mod, .{ .int = .{
5928 .ty = ty.toIntern(),
5929 .storage = .{ .i64 = x },
5930 } });
5931 return Value.fromInterned(i);
5932}
5933
5934pub fn unionValue(mod: *Module, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value {
5935 const i = try intern(mod, .{ .un = .{
5936 .ty = union_ty.toIntern(),
5937 .tag = tag.toIntern(),
5938 .val = val.toIntern(),
5939 } });
5940 return Value.fromInterned(i);
5941}
5942
5943/// This function casts the float representation down to the representation of the type, potentially
5944/// losing data if the representation wasn't correct.
5945pub fn floatValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
5946 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(mod.getTarget())) {
5947 16 => .{ .f16 = @as(f16, @floatCast(x)) },
5948 32 => .{ .f32 = @as(f32, @floatCast(x)) },
5949 64 => .{ .f64 = @as(f64, @floatCast(x)) },
5950 80 => .{ .f80 = @as(f80, @floatCast(x)) },
5951 128 => .{ .f128 = @as(f128, @floatCast(x)) },
5952 else => unreachable,
5953 };
5954 const i = try intern(mod, .{ .float = .{
5955 .ty = ty.toIntern(),
5956 .storage = storage,
5957 } });
5958 return Value.fromInterned(i);
5959}
5960
5961pub fn nullValue(mod: *Module, opt_ty: Type) Allocator.Error!Value {
5962 const ip = &mod.intern_pool;
5963 assert(ip.isOptionalType(opt_ty.toIntern()));
5964 const result = try ip.get(mod.gpa, .{ .opt = .{
5965 .ty = opt_ty.toIntern(),
5966 .val = .none,
5967 } });
5968 return Value.fromInterned(result);
5969}
5970
5971pub fn smallestUnsignedInt(mod: *Module, max: u64) Allocator.Error!Type {
5972 return intType(mod, .unsigned, Type.smallestUnsignedBits(max));
5973}
5974
5975/// Returns the smallest possible integer type containing both `min` and
5976/// `max`. Asserts that neither value is undef.
5977/// TODO: if #3806 is implemented, this becomes trivial
5978pub fn intFittingRange(mod: *Module, min: Value, max: Value) !Type {
5979 assert(!min.isUndef(mod));
5980 assert(!max.isUndef(mod));
5981
5982 if (std.debug.runtime_safety) {
5983 assert(Value.order(min, max, mod).compare(.lte));
5984 }
5985
5986 const sign = min.orderAgainstZero(mod) == .lt;
5987
5988 const min_val_bits = intBitsForValue(mod, min, sign);
5989 const max_val_bits = intBitsForValue(mod, max, sign);
5990
5991 return mod.intType(
5992 if (sign) .signed else .unsigned,
5993 @max(min_val_bits, max_val_bits),
5994 );
5995}
5996
5997/// Given a value representing an integer, returns the number of bits necessary to represent
5998/// this value in an integer. If `sign` is true, returns the number of bits necessary in a
5999/// twos-complement integer; otherwise in an unsigned integer.
6000/// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true.
6001pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
6002 assert(!val.isUndef(mod));
6003
6004 const key = mod.intern_pool.indexToKey(val.toIntern());
6005 switch (key.int.storage) {
6006 .i64 => |x| {
6007 if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted) + @intFromBool(sign);
6008 assert(sign);
6009 // Protect against overflow in the following negation.
6010 if (x == std.math.minInt(i64)) return 64;
6011 return Type.smallestUnsignedBits(@as(u64, @intCast(-(x + 1)))) + 1;
6012 },
6013 .u64 => |x| {
6014 return Type.smallestUnsignedBits(x) + @intFromBool(sign);
6015 },
6016 .big_int => |big| {
6017 if (big.positive) return @as(u16, @intCast(big.bitCountAbs() + @intFromBool(sign)));
6018
6019 // Zero is still a possibility, in which case unsigned is fine
6020 if (big.eqlZero()) return 0;
6021
6022 return @as(u16, @intCast(big.bitCountTwosComp()));
6023 },
6024 .lazy_align => |lazy_ty| {
6025 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(mod).toByteUnits() orelse 0) + @intFromBool(sign);
6026 },
6027 .lazy_size => |lazy_ty| {
6028 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(mod)) + @intFromBool(sign);
6029 },
6030 }
6031}
6032
6033pub const AtomicPtrAlignmentError = error{
6034 FloatTooBig,
6035 IntTooBig,
6036 BadType,
6037 OutOfMemory,
6038};
6039
6040pub const AtomicPtrAlignmentDiagnostics = struct {
6041 bits: u16 = undefined,
6042 max_bits: u16 = undefined,
6043};
6044
6045/// If ABI alignment of `ty` is OK for atomic operations, returns 0.
6046/// Otherwise returns the alignment required on a pointer for the target
6047/// to perform atomic operations.
6048// TODO this function does not take into account CPU features, which can affect
6049// this value. Audit this!
6050pub fn atomicPtrAlignment(
6051 mod: *Module,
6052 ty: Type,
6053 diags: *AtomicPtrAlignmentDiagnostics,
6054) AtomicPtrAlignmentError!Alignment {
6055 const target = mod.getTarget();
6056 const max_atomic_bits: u16 = switch (target.cpu.arch) {
6057 .avr,
6058 .msp430,
6059 .spu_2,
6060 => 16,
6061
6062 .arc,
6063 .arm,
6064 .armeb,
6065 .hexagon,
6066 .m68k,
6067 .le32,
6068 .mips,
6069 .mipsel,
6070 .nvptx,
6071 .powerpc,
6072 .powerpcle,
6073 .r600,
6074 .riscv32,
6075 .sparc,
6076 .sparcel,
6077 .tce,
6078 .tcele,
6079 .thumb,
6080 .thumbeb,
6081 .x86,
6082 .xcore,
6083 .amdil,
6084 .hsail,
6085 .spir,
6086 .kalimba,
6087 .lanai,
6088 .shave,
6089 .wasm32,
6090 .renderscript32,
6091 .csky,
6092 .spirv32,
6093 .dxil,
6094 .loongarch32,
6095 .xtensa,
6096 => 32,
6097
6098 .amdgcn,
6099 .bpfel,
6100 .bpfeb,
6101 .le64,
6102 .mips64,
6103 .mips64el,
6104 .nvptx64,
6105 .powerpc64,
6106 .powerpc64le,
6107 .riscv64,
6108 .sparc64,
6109 .s390x,
6110 .amdil64,
6111 .hsail64,
6112 .spir64,
6113 .wasm64,
6114 .renderscript64,
6115 .ve,
6116 .spirv64,
6117 .loongarch64,
6118 => 64,
6119
6120 .aarch64,
6121 .aarch64_be,
6122 .aarch64_32,
6123 => 128,
6124
6125 .x86_64 => if (std.Target.x86.featureSetHas(target.cpu.features, .cx16)) 128 else 64,
6126
6127 .spirv => @panic("TODO what should this value be?"),
6128 };
6129
6130 const int_ty = switch (ty.zigTypeTag(mod)) {
6131 .Int => ty,
6132 .Enum => ty.intTagType(mod),
6133 .Float => {
6134 const bit_count = ty.floatBits(target);
6135 if (bit_count > max_atomic_bits) {
6136 diags.* = .{
6137 .bits = bit_count,
6138 .max_bits = max_atomic_bits,
6139 };
6140 return error.FloatTooBig;
6141 }
6142 return .none;
6143 },
6144 .Bool => return .none,
6145 else => {
6146 if (ty.isPtrAtRuntime(mod)) return .none;
6147 return error.BadType;
6148 },
6149 };
6150
6151 const bit_count = int_ty.intInfo(mod).bits;
6152 if (bit_count > max_atomic_bits) {
6153 diags.* = .{
6154 .bits = bit_count,
6155 .max_bits = max_atomic_bits,
6156 };
6157 return error.IntTooBig;
6158 }
6159
6160 return .none;
6161}
6162
6163pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
6164 return mod.declPtr(decl_index).getFileScope(mod);
6165}
6166
6167/// Returns null in the following cases:
6168/// * `@TypeOf(.{})`
6169/// * A struct which has no fields (`struct {}`).
6170/// * Not a struct.
6171pub fn typeToStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
6172 if (ty.ip_index == .none) return null;
6173 const ip = &mod.intern_pool;
6174 return switch (ip.indexToKey(ty.ip_index)) {
6175 .struct_type => ip.loadStructType(ty.ip_index),
6176 else => null,
6177 };
6178}
6179
6180pub fn typeToPackedStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
6181 const s = mod.typeToStruct(ty) orelse return null;
6182 if (s.layout != .@"packed") return null;
6183 return s;
6184}
6185
6186pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.LoadedUnionType {
6187 if (ty.ip_index == .none) return null;
6188 const ip = &mod.intern_pool;
6189 return switch (ip.indexToKey(ty.ip_index)) {
6190 .union_type => ip.loadUnionType(ty.ip_index),
6191 else => null,
6192 };
6193}
6194
6195pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType {
6196 if (ty.ip_index == .none) return null;
6197 return mod.intern_pool.indexToFuncType(ty.toIntern());
6198}
6199
6200pub fn funcOwnerDeclPtr(mod: *Module, func_index: InternPool.Index) *Decl {
6201 return mod.declPtr(mod.funcOwnerDeclIndex(func_index));
6202}
6203
6204pub fn funcOwnerDeclIndex(mod: *Module, func_index: InternPool.Index) Decl.Index {
6205 return mod.funcInfo(func_index).owner_decl;
6206}
6207
6208pub fn iesFuncIndex(mod: *const Module, ies_index: InternPool.Index) InternPool.Index {
6209 return mod.intern_pool.iesFuncIndex(ies_index);
6210}
6211
6212pub fn funcInfo(mod: *Module, func_index: InternPool.Index) InternPool.Key.Func {
6213 return mod.intern_pool.indexToKey(func_index).func;
6214}
6215
6216pub fn toEnum(mod: *Module, comptime E: type, val: Value) E {
6217 return mod.intern_pool.toEnum(E, val.toIntern());
6218}
6219
6220pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool {
6221 const file = mod.declPtr(func.owner_decl).getFileScope(mod);
6222
6223 const tags = file.zir.instructions.items(.tag);
6224
6225 const param_body = file.zir.getParamBody(func.zir_body_inst);
6226 const param = param_body[index];
6227
6228 return switch (tags[param]) {
6229 .param, .param_comptime => false,
6230 .param_anytype, .param_anytype_comptime => true,
6231 else => unreachable,
6232 };
6233}
6234
6235pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]const u8 {
6236 const func = mod.funcInfo(func_index);
6237 const file = mod.declPtr(func.owner_decl).getFileScope(mod);
6238
6239 const tags = file.zir.instructions.items(.tag);
6240 const data = file.zir.instructions.items(.data);
6241
6242 const param_body = file.zir.getParamBody(func.zir_body_inst.resolve(&mod.intern_pool));
6243 const param = param_body[index];
6244
6245 return switch (tags[@intFromEnum(param)]) {
6246 .param, .param_comptime => blk: {
6247 const extra = file.zir.extraData(Zir.Inst.Param, data[@intFromEnum(param)].pl_tok.payload_index);
6248 break :blk file.zir.nullTerminatedString(extra.data.name);
6249 },
6250 .param_anytype, .param_anytype_comptime => blk: {
6251 const param_data = data[@intFromEnum(param)].str_tok;
6252 break :blk param_data.get(file.zir);
6253 },
6254 else => unreachable,
6255 };
6256}
6257
6258pub const UnionLayout = struct {
6259 abi_size: u64,
6260 abi_align: Alignment,
6261 most_aligned_field: u32,
6262 most_aligned_field_size: u64,
6263 biggest_field: u32,
6264 payload_size: u64,
6265 payload_align: Alignment,
6266 tag_align: Alignment,
6267 tag_size: u64,
6268 padding: u32,
6269};
6270
6271pub fn getUnionLayout(mod: *Module, loaded_union: InternPool.LoadedUnionType) UnionLayout {
6272 const ip = &mod.intern_pool;
6273 assert(loaded_union.haveLayout(ip));
6274 var most_aligned_field: u32 = undefined;
6275 var most_aligned_field_size: u64 = undefined;
6276 var biggest_field: u32 = undefined;
6277 var payload_size: u64 = 0;
6278 var payload_align: Alignment = .@"1";
6279 for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
6280 if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue;
6281
6282 const explicit_align = loaded_union.fieldAlign(ip, field_index);
6283 const field_align = if (explicit_align != .none)
6284 explicit_align
6285 else
6286 Type.fromInterned(field_ty).abiAlignment(mod);
6287 const field_size = Type.fromInterned(field_ty).abiSize(mod);
6288 if (field_size > payload_size) {
6289 payload_size = field_size;
6290 biggest_field = @intCast(field_index);
6291 }
6292 if (field_align.compare(.gte, payload_align)) {
6293 payload_align = field_align;
6294 most_aligned_field = @intCast(field_index);
6295 most_aligned_field_size = field_size;
6296 }
6297 }
6298 const have_tag = loaded_union.flagsPtr(ip).runtime_tag.hasTag();
6299 if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(mod)) {
6300 return .{
6301 .abi_size = payload_align.forward(payload_size),
6302 .abi_align = payload_align,
6303 .most_aligned_field = most_aligned_field,
6304 .most_aligned_field_size = most_aligned_field_size,
6305 .biggest_field = biggest_field,
6306 .payload_size = payload_size,
6307 .payload_align = payload_align,
6308 .tag_align = .none,
6309 .tag_size = 0,
6310 .padding = 0,
6311 };
6312 }
6313
6314 const tag_size = Type.fromInterned(loaded_union.enum_tag_ty).abiSize(mod);
6315 const tag_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(mod).max(.@"1");
6316 return .{
6317 .abi_size = loaded_union.size(ip).*,
6318 .abi_align = tag_align.max(payload_align),
6319 .most_aligned_field = most_aligned_field,
6320 .most_aligned_field_size = most_aligned_field_size,
6321 .biggest_field = biggest_field,
6322 .payload_size = payload_size,
6323 .payload_align = payload_align,
6324 .tag_align = tag_align,
6325 .tag_size = tag_size,
6326 .padding = loaded_union.padding(ip).*,
6327 };
6328}
6329
6330pub fn unionAbiSize(mod: *Module, loaded_union: InternPool.LoadedUnionType) u64 {
6331 return mod.getUnionLayout(loaded_union).abi_size;
6332}
6333
6334/// Returns 0 if the union is represented with 0 bits at runtime.
6335pub fn unionAbiAlignment(mod: *Module, loaded_union: InternPool.LoadedUnionType) Alignment {
6336 const ip = &mod.intern_pool;
6337 const have_tag = loaded_union.flagsPtr(ip).runtime_tag.hasTag();
6338 var max_align: Alignment = .none;
6339 if (have_tag) max_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(mod);
6340 for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
6341 if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue;
6342
6343 const field_align = mod.unionFieldNormalAlignment(loaded_union, @intCast(field_index));
6344 max_align = max_align.max(field_align);
6345 }
6346 return max_align;
6347}
6348
6349/// Returns the field alignment, assuming the union is not packed.
6350/// Keep implementation in sync with `Sema.unionFieldAlignment`.
6351/// Prefer to call that function instead of this one during Sema.
6352pub fn unionFieldNormalAlignment(mod: *Module, loaded_union: InternPool.LoadedUnionType, field_index: u32) Alignment {
6353 const ip = &mod.intern_pool;
6354 const field_align = loaded_union.fieldAlign(ip, field_index);
6355 if (field_align != .none) return field_align;
6356 const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]);
6357 return field_ty.abiAlignment(mod);
6358}
6359
6360/// Returns the index of the active field, given the current tag value
6361pub fn unionTagFieldIndex(mod: *Module, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
6362 const ip = &mod.intern_pool;
6363 if (enum_tag.toIntern() == .none) return null;
6364 assert(ip.typeOf(enum_tag.toIntern()) == loaded_union.enum_tag_ty);
6365 return loaded_union.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern());
6366}
6367
6368/// Returns the field alignment of a non-packed struct in byte units.
6369/// Keep implementation in sync with `Sema.structFieldAlignment`.
6370/// asserts the layout is not packed.
6371pub fn structFieldAlignment(
6372 mod: *Module,
6373 explicit_alignment: InternPool.Alignment,
6374 field_ty: Type,
6375 layout: std.builtin.Type.ContainerLayout,
6376) Alignment {
6377 assert(layout != .@"packed");
6378 if (explicit_alignment != .none) return explicit_alignment;
6379 switch (layout) {
6380 .@"packed" => unreachable,
6381 .auto => {
6382 if (mod.getTarget().ofmt == .c) {
6383 return structFieldAlignmentExtern(mod, field_ty);
6384 } else {
6385 return field_ty.abiAlignment(mod);
6386 }
6387 },
6388 .@"extern" => return structFieldAlignmentExtern(mod, field_ty),
6389 }
6390}
6391
6392/// Returns the field alignment of an extern struct in byte units.
6393/// This logic is duplicated in Type.abiAlignmentAdvanced.
6394pub fn structFieldAlignmentExtern(mod: *Module, field_ty: Type) Alignment {
6395 const ty_abi_align = field_ty.abiAlignment(mod);
6396
6397 if (field_ty.isAbiInt(mod) and field_ty.intInfo(mod).bits >= 128) {
6398 // The C ABI requires 128 bit integer fields of structs
6399 // to be 16-bytes aligned.
6400 return ty_abi_align.max(.@"16");
6401 }
6402
6403 return ty_abi_align;
6404}
6405
6406/// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets
6407/// into the packed struct InternPool data rather than computing this on the
6408/// fly, however it was found to perform worse when measured on real world
6409/// projects.
6410pub fn structPackedFieldBitOffset(
6411 mod: *Module,
6412 struct_type: InternPool.LoadedStructType,
6413 field_index: u32,
6414) u16 {
6415 const ip = &mod.intern_pool;
6416 assert(struct_type.layout == .@"packed");
6417 assert(struct_type.haveLayout(ip));
6418 var bit_sum: u64 = 0;
6419 for (0..struct_type.field_types.len) |i| {
6420 if (i == field_index) {
6421 return @intCast(bit_sum);
6422 }
6423 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
6424 bit_sum += field_ty.bitSize(mod);
6425 }
6426 unreachable; // index out of bounds
6427}
src/Package/Module.zig+1-3
......@@ -1,6 +1,4 @@
11//! Corresponds to something that Zig source code can `@import`.
2//! Not to be confused with src/Module.zig which will be renamed
3//! to Zcu. https://github.com/ziglang/zig/issues/14307
42
53/// Only files inside this directory can be imported.
64root: Cache.Path,
......@@ -518,4 +516,4 @@ const Cache = std.Build.Cache;
518516const Builtin = @import("../Builtin.zig");
519517const assert = std.debug.assert;
520518const Compilation = @import("../Compilation.zig");
521const File = @import("../Module.zig").File;
519const File = @import("../Zcu.zig").File;
src/RangeSet.zig+4-2
......@@ -5,9 +5,11 @@ const Order = std.math.Order;
55const InternPool = @import("InternPool.zig");
66const Type = @import("type.zig").Type;
77const Value = @import("Value.zig");
8const Module = @import("Module.zig");
8const Zcu = @import("Zcu.zig");
9/// Deprecated.
10const Module = Zcu;
911const RangeSet = @This();
10const LazySrcLoc = @import("Module.zig").LazySrcLoc;
12const LazySrcLoc = Zcu.LazySrcLoc;
1113
1214ranges: std.ArrayList(Range),
1315module: *Module,
src/Sema.zig+1-1
......@@ -170,7 +170,7 @@ const MutableValue = @import("mutable_value.zig").MutableValue;
170170const Type = @import("type.zig").Type;
171171const Air = @import("Air.zig");
172172const Zir = std.zig.Zir;
173const Zcu = @import("Module.zig");
173const Zcu = @import("Zcu.zig");
174174const Module = Zcu;
175175const trace = @import("tracy.zig").trace;
176176const Namespace = Module.Namespace;
src/Sema/bitcast.zig+1-1
......@@ -765,7 +765,7 @@ const Allocator = std.mem.Allocator;
765765const assert = std.debug.assert;
766766
767767const Sema = @import("../Sema.zig");
768const Zcu = @import("../Module.zig");
768const Zcu = @import("../Zcu.zig");
769769const InternPool = @import("../InternPool.zig");
770770const Type = @import("../type.zig").Type;
771771const Value = @import("../Value.zig");
src/Sema/comptime_ptr_access.zig+1-1
......@@ -1056,5 +1056,5 @@ const Block = Sema.Block;
10561056const MutableValue = @import("../mutable_value.zig").MutableValue;
10571057const Type = @import("../type.zig").Type;
10581058const Value = @import("../Value.zig");
1059const Zcu = @import("../Module.zig");
1059const Zcu = @import("../Zcu.zig");
10601060const LazySrcLoc = Zcu.LazySrcLoc;
src/Value.zig+2-1
......@@ -6,7 +6,8 @@ const BigIntConst = std.math.big.int.Const;
66const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
9const Zcu = @import("Module.zig");
9const Zcu = @import("Zcu.zig");
10/// Deprecated.
1011const Module = Zcu;
1112const Sema = @import("Sema.zig");
1213const InternPool = @import("InternPool.zig");
src/Zcu.zig created+6427
......@@ -0,0 +1,6427 @@
1//! Compilation of all Zig source code is represented by one `Module`.
2//! Each `Compilation` has exactly one or zero `Module`, depending on whether
3//! there is or is not any zig source code, respectively.
4
5const std = @import("std");
6const builtin = @import("builtin");
7const mem = std.mem;
8const Allocator = std.mem.Allocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const assert = std.debug.assert;
11const log = std.log.scoped(.module);
12const BigIntConst = std.math.big.int.Const;
13const BigIntMutable = std.math.big.int.Mutable;
14const Target = std.Target;
15const Ast = std.zig.Ast;
16
17/// Deprecated, use `Zcu`.
18const Module = Zcu;
19const Zcu = @This();
20const Compilation = @import("Compilation.zig");
21const Cache = std.Build.Cache;
22const Value = @import("Value.zig");
23const Type = @import("type.zig").Type;
24const Package = @import("Package.zig");
25const link = @import("link.zig");
26const Air = @import("Air.zig");
27const Zir = std.zig.Zir;
28const trace = @import("tracy.zig").trace;
29const AstGen = std.zig.AstGen;
30const Sema = @import("Sema.zig");
31const target_util = @import("target.zig");
32const build_options = @import("build_options");
33const Liveness = @import("Liveness.zig");
34const isUpDir = @import("introspect.zig").isUpDir;
35const clang = @import("clang.zig");
36const InternPool = @import("InternPool.zig");
37const Alignment = InternPool.Alignment;
38const BuiltinFn = std.zig.BuiltinFn;
39const LlvmObject = @import("codegen/llvm.zig").Object;
40
41comptime {
42 @setEvalBranchQuota(4000);
43 for (
44 @typeInfo(Zir.Inst.Ref).Enum.fields,
45 @typeInfo(Air.Inst.Ref).Enum.fields,
46 @typeInfo(InternPool.Index).Enum.fields,
47 ) |zir_field, air_field, ip_field| {
48 assert(mem.eql(u8, zir_field.name, ip_field.name));
49 assert(mem.eql(u8, air_field.name, ip_field.name));
50 }
51}
52
53/// General-purpose allocator. Used for both temporary and long-term storage.
54gpa: Allocator,
55comp: *Compilation,
56/// Usually, the LlvmObject is managed by linker code, however, in the case
57/// that -fno-emit-bin is specified, the linker code never executes, so we
58/// store the LlvmObject here.
59llvm_object: ?*LlvmObject,
60
61/// Pointer to externally managed resource.
62root_mod: *Package.Module,
63/// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which
64/// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests.
65main_mod: *Package.Module,
66std_mod: *Package.Module,
67sema_prog_node: std.Progress.Node = undefined,
68codegen_prog_node: std.Progress.Node = undefined,
69
70/// Used by AstGen worker to load and store ZIR cache.
71global_zir_cache: Compilation.Directory,
72/// Used by AstGen worker to load and store ZIR cache.
73local_zir_cache: Compilation.Directory,
74/// It's rare for a decl to be exported, so we save memory by having a sparse
75/// map of Decl indexes to details about them being exported.
76/// The Export memory is owned by the `export_owners` table; the slice itself
77/// is owned by this table. The slice is guaranteed to not be empty.
78decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
79/// Same as `decl_exports` but for exported constant values.
80value_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, ArrayListUnmanaged(*Export)) = .{},
81/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
82/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
83/// is performing the export of another Decl.
84/// This table owns the Export memory.
85export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
86/// The set of all the Zig source files in the Module. We keep track of this in order
87/// to iterate over it and check which source files have been modified on the file system when
88/// an update is requested, as well as to cache `@import` results.
89/// Keys are fully resolved file paths. This table owns the keys and values.
90import_table: std.StringArrayHashMapUnmanaged(*File) = .{},
91/// This acts as a map from `path_digest` to the corresponding `File`.
92/// The value is omitted, as keys are ordered identically to `import_table`.
93path_digest_map: std.AutoArrayHashMapUnmanaged(Cache.BinDigest, void) = .{},
94/// The set of all the files which have been loaded with `@embedFile` in the Module.
95/// We keep track of this in order to iterate over it and check which files have been
96/// modified on the file system when an update is requested, as well as to cache
97/// `@embedFile` results.
98/// Keys are fully resolved file paths. This table owns the keys and values.
99embed_table: std.StringArrayHashMapUnmanaged(*EmbedFile) = .{},
100
101/// Stores all Type and Value objects.
102/// The idea is that this will be periodically garbage-collected, but such logic
103/// is not yet implemented.
104intern_pool: InternPool = .{},
105
106/// We optimize memory usage for a compilation with no compile errors by storing the
107/// error messages and mapping outside of `Decl`.
108/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
109/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
110/// a Decl can have a failed_decls entry but have analysis status of success.
111failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{},
112/// Keep track of one `@compileLog` callsite per owner Decl.
113/// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`.
114compile_log_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, extern struct {
115 base_node_inst: InternPool.TrackedInst.Index,
116 node_offset: i32,
117 pub fn src(self: @This()) LazySrcLoc {
118 return .{
119 .base_node_inst = self.base_node_inst,
120 .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset),
121 };
122 }
123}) = .{},
124/// Using a map here for consistency with the other fields here.
125/// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator.
126failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{},
127/// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator.
128failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{},
129/// Using a map here for consistency with the other fields here.
130/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
131failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
132/// If a decl failed due to a cimport error, the corresponding Clang errors
133/// are stored here.
134cimport_errors: std.AutoArrayHashMapUnmanaged(Decl.Index, std.zig.ErrorBundle) = .{},
135
136/// Key is the error name, index is the error tag value. Index 0 has a length-0 string.
137global_error_set: GlobalErrorSet = .{},
138
139/// Maximum amount of distinct error values, set by --error-limit
140error_limit: ErrorInt,
141
142/// Value is the number of PO or outdated Decls which this Depender depends on.
143potentially_outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{},
144/// Value is the number of PO or outdated Decls which this Depender depends on.
145/// Once this value drops to 0, the Depender is a candidate for re-analysis.
146outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{},
147/// This contains all `Depender`s in `outdated` whose PO dependency count is 0.
148/// Such `Depender`s are ready for immediate re-analysis.
149/// See `findOutdatedToAnalyze` for details.
150outdated_ready: std.AutoArrayHashMapUnmanaged(InternPool.Depender, void) = .{},
151/// This contains a set of Decls which may not be in `outdated`, but are the
152/// root Decls of files which have updated source and thus must be re-analyzed.
153/// If such a Decl is only in this set, the struct type index may be preserved
154/// (only the namespace might change). If such a Decl is also `outdated`, the
155/// struct type index must be recreated.
156outdated_file_root: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
157/// This contains a list of Dependers whose analysis or codegen failed, but the
158/// failure was something like running out of disk space, and trying again may
159/// succeed. On the next update, we will flush this list, marking all members of
160/// it as outdated.
161retryable_failures: std.ArrayListUnmanaged(InternPool.Depender) = .{},
162
163stage1_flags: packed struct {
164 have_winmain: bool = false,
165 have_wwinmain: bool = false,
166 have_winmain_crt_startup: bool = false,
167 have_wwinmain_crt_startup: bool = false,
168 have_dllmain_crt_startup: bool = false,
169 have_c_main: bool = false,
170 reserved: u2 = 0,
171} = .{},
172
173compile_log_text: ArrayListUnmanaged(u8) = .{},
174
175emit_h: ?*GlobalEmitH,
176
177test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
178
179global_assembly: std.AutoArrayHashMapUnmanaged(Decl.Index, []u8) = .{},
180
181reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {
182 referencer: Decl.Index,
183 src: LazySrcLoc,
184}) = .{},
185
186panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len,
187/// The panic function body.
188panic_func_index: InternPool.Index = .none,
189null_stack_trace: InternPool.Index = .none,
190
191pub const PanicId = enum {
192 unreach,
193 unwrap_null,
194 cast_to_null,
195 incorrect_alignment,
196 invalid_error_code,
197 cast_truncated_data,
198 negative_to_unsigned,
199 integer_overflow,
200 shl_overflow,
201 shr_overflow,
202 divide_by_zero,
203 exact_division_remainder,
204 inactive_union_field,
205 integer_part_out_of_bounds,
206 corrupt_switch,
207 shift_rhs_too_big,
208 invalid_enum_value,
209 sentinel_mismatch,
210 unwrap_error,
211 index_out_of_bounds,
212 start_index_greater_than_end,
213 for_len_mismatch,
214 memcpy_len_mismatch,
215 memcpy_alias,
216 noreturn_returned,
217
218 pub const len = @typeInfo(PanicId).Enum.fields.len;
219};
220
221pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
222
223pub const CImportError = struct {
224 offset: u32,
225 line: u32,
226 column: u32,
227 path: ?[*:0]u8,
228 source_line: ?[*:0]u8,
229 msg: [*:0]u8,
230
231 pub fn deinit(err: CImportError, gpa: Allocator) void {
232 if (err.path) |some| gpa.free(std.mem.span(some));
233 if (err.source_line) |some| gpa.free(std.mem.span(some));
234 gpa.free(std.mem.span(err.msg));
235 }
236};
237
238/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
239pub const GlobalEmitH = struct {
240 /// Where to put the output.
241 loc: Compilation.EmitLoc,
242 /// When emit_h is non-null, each Decl gets one more compile error slot for
243 /// emit-h failing for that Decl. This table is also how we tell if a Decl has
244 /// failed emit-h or succeeded.
245 failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{},
246 /// Tracks all decls in order to iterate over them and emit .h code for them.
247 decl_table: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
248 /// Similar to the allocated_decls field of Module, this is where `EmitH` objects
249 /// are allocated. There will be exactly one EmitH object per Decl object, with
250 /// identical indexes.
251 allocated_emit_h: std.SegmentedList(EmitH, 0) = .{},
252
253 pub fn declPtr(global_emit_h: *GlobalEmitH, decl_index: Decl.Index) *EmitH {
254 return global_emit_h.allocated_emit_h.at(@intFromEnum(decl_index));
255 }
256};
257
258pub const ErrorInt = u32;
259
260pub const Exported = union(enum) {
261 /// The Decl being exported. Note this is *not* the Decl performing the export.
262 decl_index: Decl.Index,
263 /// Constant value being exported.
264 value: InternPool.Index,
265};
266
267pub const Export = struct {
268 opts: Options,
269 src: LazySrcLoc,
270 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
271 owner_decl: Decl.Index,
272 exported: Exported,
273 status: enum {
274 in_progress,
275 failed,
276 /// Indicates that the failure was due to a temporary issue, such as an I/O error
277 /// when writing to the output file. Retrying the export may succeed.
278 failed_retryable,
279 complete,
280 },
281
282 pub const Options = struct {
283 name: InternPool.NullTerminatedString,
284 linkage: std.builtin.GlobalLinkage = .strong,
285 section: InternPool.OptionalNullTerminatedString = .none,
286 visibility: std.builtin.SymbolVisibility = .default,
287 };
288
289 pub fn getSrcLoc(exp: Export, mod: *Module) SrcLoc {
290 return exp.src.upgrade(mod);
291 }
292};
293
294const ValueArena = struct {
295 state: std.heap.ArenaAllocator.State,
296 state_acquired: ?*std.heap.ArenaAllocator.State = null,
297
298 /// If this ValueArena replaced an existing one during re-analysis, this is the previous instance
299 prev: ?*ValueArena = null,
300
301 /// Returns an allocator backed by either promoting `state`, or by the existing ArenaAllocator
302 /// that has already promoted `state`. `out_arena_allocator` provides storage for the initial promotion,
303 /// and must live until the matching call to release().
304 pub fn acquire(self: *ValueArena, child_allocator: Allocator, out_arena_allocator: *std.heap.ArenaAllocator) Allocator {
305 if (self.state_acquired) |state_acquired| {
306 return @as(*std.heap.ArenaAllocator, @fieldParentPtr("state", state_acquired)).allocator();
307 }
308
309 out_arena_allocator.* = self.state.promote(child_allocator);
310 self.state_acquired = &out_arena_allocator.state;
311 return out_arena_allocator.allocator();
312 }
313
314 /// Releases the allocator acquired by `acquire. `arena_allocator` must match the one passed to `acquire`.
315 pub fn release(self: *ValueArena, arena_allocator: *std.heap.ArenaAllocator) void {
316 if (@as(*std.heap.ArenaAllocator, @fieldParentPtr("state", self.state_acquired.?)) == arena_allocator) {
317 self.state = self.state_acquired.?.*;
318 self.state_acquired = null;
319 }
320 }
321
322 pub fn deinit(self: ValueArena, child_allocator: Allocator) void {
323 assert(self.state_acquired == null);
324
325 const prev = self.prev;
326 self.state.promote(child_allocator).deinit();
327
328 if (prev) |p| {
329 p.deinit(child_allocator);
330 }
331 }
332};
333
334pub const Decl = struct {
335 name: InternPool.NullTerminatedString,
336 /// The most recent Value of the Decl after a successful semantic analysis.
337 /// Populated when `has_tv`.
338 val: Value,
339 /// Populated when `has_tv`.
340 @"linksection": InternPool.OptionalNullTerminatedString,
341 /// Populated when `has_tv`.
342 alignment: Alignment,
343 /// Populated when `has_tv`.
344 @"addrspace": std.builtin.AddressSpace,
345 /// The direct parent namespace of the Decl. In the case of the Decl
346 /// corresponding to a file, this is the namespace of the struct, since
347 /// there is no parent.
348 src_namespace: Namespace.Index,
349
350 /// Line number corresponding to `src_node`. Stored separately so that source files
351 /// do not need to be loaded into memory in order to compute debug line numbers.
352 /// This value is absolute.
353 src_line: u32,
354 /// Index of the ZIR `declaration` instruction from which this `Decl` was created.
355 /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`.
356 zir_decl_index: InternPool.TrackedInst.Index.Optional,
357
358 /// Represents the "shallow" analysis status. For example, for decls that are functions,
359 /// the function type is analyzed with this set to `in_progress`, however, the semantic
360 /// analysis of the function body is performed with this value set to `success`. Functions
361 /// have their own analysis status field.
362 analysis: enum {
363 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
364 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
365 unreferenced,
366 /// Semantic analysis for this Decl is running right now.
367 /// This state detects dependency loops.
368 in_progress,
369 /// The file corresponding to this Decl had a parse error or ZIR error.
370 /// There will be a corresponding ErrorMsg in Zcu.failed_files.
371 file_failure,
372 /// This Decl might be OK but it depends on another one which did not
373 /// successfully complete semantic analysis.
374 dependency_failure,
375 /// Semantic analysis failure.
376 /// There will be a corresponding ErrorMsg in Zcu.failed_decls.
377 sema_failure,
378 /// There will be a corresponding ErrorMsg in Zcu.failed_decls.
379 codegen_failure,
380 /// Sematic analysis and constant value codegen of this Decl has
381 /// succeeded. However, the Decl may be outdated due to an in-progress
382 /// update. Note that for a function, this does not mean codegen of the
383 /// function body succeded: that state is indicated by the function's
384 /// `analysis` field.
385 complete,
386 },
387 /// Whether `typed_value`, `align`, `linksection` and `addrspace` are populated.
388 has_tv: bool,
389 /// If `true` it means the `Decl` is the resource owner of the type/value associated
390 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally
391 /// check if the value owns a `Namespace`, and destroy that too.
392 owns_tv: bool,
393 /// Whether the corresponding AST decl has a `pub` keyword.
394 is_pub: bool,
395 /// Whether the corresponding AST decl has a `export` keyword.
396 is_exported: bool,
397 /// If true `name` is already fully qualified.
398 name_fully_qualified: bool = false,
399 /// What kind of a declaration is this.
400 kind: Kind,
401
402 pub const Kind = enum {
403 @"usingnamespace",
404 @"test",
405 @"comptime",
406 named,
407 anon,
408 };
409
410 const Index = InternPool.DeclIndex;
411 const OptionalIndex = InternPool.OptionalDeclIndex;
412
413 pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies {
414 const zir = decl.getFileScope(zcu).zir;
415 const zir_index = decl.zir_decl_index.unwrap().?.resolve(&zcu.intern_pool);
416 const declaration = zir.instructions.items(.data)[@intFromEnum(zir_index)].declaration;
417 const extra = zir.extraData(Zir.Inst.Declaration, declaration.payload_index);
418 return extra.data.getBodies(@intCast(extra.end), zir);
419 }
420
421 pub fn renderFullyQualifiedName(decl: Decl, zcu: *Zcu, writer: anytype) !void {
422 if (decl.name_fully_qualified) {
423 try writer.print("{}", .{decl.name.fmt(&zcu.intern_pool)});
424 } else {
425 try zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedName(zcu, decl.name, writer);
426 }
427 }
428
429 pub fn renderFullyQualifiedDebugName(decl: Decl, zcu: *Zcu, writer: anytype) !void {
430 return zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedDebugName(zcu, decl.name, writer);
431 }
432
433 pub fn fullyQualifiedName(decl: Decl, zcu: *Zcu) !InternPool.NullTerminatedString {
434 return if (decl.name_fully_qualified)
435 decl.name
436 else
437 zcu.namespacePtr(decl.src_namespace).fullyQualifiedName(zcu, decl.name);
438 }
439
440 pub fn typeOf(decl: Decl, zcu: *const Zcu) Type {
441 assert(decl.has_tv);
442 return decl.val.typeOf(zcu);
443 }
444
445 /// Small wrapper for Sema to use over direct access to the `val` field.
446 /// If the value is not populated, instead returns `error.AnalysisFail`.
447 pub fn valueOrFail(decl: Decl) error{AnalysisFail}!Value {
448 if (!decl.has_tv) return error.AnalysisFail;
449 return decl.val;
450 }
451
452 pub fn getOwnedFunction(decl: Decl, zcu: *Zcu) ?InternPool.Key.Func {
453 const i = decl.getOwnedFunctionIndex();
454 if (i == .none) return null;
455 return switch (zcu.intern_pool.indexToKey(i)) {
456 .func => |func| func,
457 else => null,
458 };
459 }
460
461 /// This returns an InternPool.Index even when the value is not a function.
462 pub fn getOwnedFunctionIndex(decl: Decl) InternPool.Index {
463 return if (decl.owns_tv) decl.val.toIntern() else .none;
464 }
465
466 /// If the Decl owns its value and it is an extern function, returns it,
467 /// otherwise null.
468 pub fn getOwnedExternFunc(decl: Decl, zcu: *Zcu) ?InternPool.Key.ExternFunc {
469 return if (decl.owns_tv) decl.val.getExternFunc(zcu) else null;
470 }
471
472 /// If the Decl owns its value and it is a variable, returns it,
473 /// otherwise null.
474 pub fn getOwnedVariable(decl: Decl, zcu: *Zcu) ?InternPool.Key.Variable {
475 return if (decl.owns_tv) decl.val.getVariable(zcu) else null;
476 }
477
478 /// Gets the namespace that this Decl creates by being a struct, union,
479 /// enum, or opaque.
480 pub fn getInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex {
481 if (!decl.has_tv) return .none;
482 const ip = &zcu.intern_pool;
483 return switch (decl.val.ip_index) {
484 .empty_struct_type => .none,
485 .none => .none,
486 else => switch (ip.indexToKey(decl.val.toIntern())) {
487 .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace,
488 .struct_type => ip.loadStructType(decl.val.toIntern()).namespace,
489 .union_type => ip.loadUnionType(decl.val.toIntern()).namespace,
490 .enum_type => ip.loadEnumType(decl.val.toIntern()).namespace,
491 else => .none,
492 },
493 };
494 }
495
496 /// Like `getInnerNamespaceIndex`, but only returns it if the Decl is the owner.
497 pub fn getOwnedInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex {
498 if (!decl.owns_tv) return .none;
499 return decl.getInnerNamespaceIndex(zcu);
500 }
501
502 /// Same as `getOwnedInnerNamespaceIndex` but additionally obtains the pointer.
503 pub fn getOwnedInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace {
504 return zcu.namespacePtrUnwrap(decl.getOwnedInnerNamespaceIndex(zcu));
505 }
506
507 /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer.
508 pub fn getInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace {
509 return zcu.namespacePtrUnwrap(decl.getInnerNamespaceIndex(zcu));
510 }
511
512 pub fn getFileScope(decl: Decl, zcu: *Zcu) *File {
513 return zcu.namespacePtr(decl.src_namespace).file_scope;
514 }
515
516 pub fn getExternDecl(decl: Decl, zcu: *Zcu) OptionalIndex {
517 assert(decl.has_tv);
518 return switch (zcu.intern_pool.indexToKey(decl.val.toIntern())) {
519 .variable => |variable| if (variable.is_extern) variable.decl.toOptional() else .none,
520 .extern_func => |extern_func| extern_func.decl.toOptional(),
521 else => .none,
522 };
523 }
524
525 pub fn isExtern(decl: Decl, zcu: *Zcu) bool {
526 return decl.getExternDecl(zcu) != .none;
527 }
528
529 pub fn getAlignment(decl: Decl, zcu: *Zcu) Alignment {
530 assert(decl.has_tv);
531 if (decl.alignment != .none) return decl.alignment;
532 return decl.typeOf(zcu).abiAlignment(zcu);
533 }
534
535 pub fn declPtrType(decl: Decl, zcu: *Zcu) !Type {
536 assert(decl.has_tv);
537 const decl_ty = decl.typeOf(zcu);
538 return zcu.ptrType(.{
539 .child = decl_ty.toIntern(),
540 .flags = .{
541 .alignment = if (decl.alignment == decl_ty.abiAlignment(zcu))
542 .none
543 else
544 decl.alignment,
545 .address_space = decl.@"addrspace",
546 .is_const = decl.getOwnedVariable(zcu) == null,
547 },
548 });
549 }
550
551 /// Returns the source location of this `Decl`.
552 /// Asserts that this `Decl` corresponds to what will in future be a `Nav` (Named
553 /// Addressable Value): a source-level declaration or generic instantiation.
554 pub fn navSrcLoc(decl: Decl, zcu: *Zcu) LazySrcLoc {
555 return .{
556 .base_node_inst = decl.zir_decl_index.unwrap() orelse inst: {
557 // generic instantiation
558 assert(decl.has_tv);
559 assert(decl.owns_tv);
560 const owner = zcu.funcInfo(decl.val.toIntern()).generic_owner;
561 const generic_owner_decl = zcu.declPtr(zcu.funcInfo(owner).owner_decl);
562 break :inst generic_owner_decl.zir_decl_index.unwrap().?;
563 },
564 .offset = LazySrcLoc.Offset.nodeOffset(0),
565 };
566 }
567};
568
569/// This state is attached to every Decl when Module emit_h is non-null.
570pub const EmitH = struct {
571 fwd_decl: ArrayListUnmanaged(u8) = .{},
572};
573
574pub const DeclAdapter = struct {
575 zcu: *Zcu,
576
577 pub fn hash(self: @This(), s: InternPool.NullTerminatedString) u32 {
578 _ = self;
579 return std.hash.uint32(@intFromEnum(s));
580 }
581
582 pub fn eql(self: @This(), a: InternPool.NullTerminatedString, b_decl_index: Decl.Index, b_index: usize) bool {
583 _ = b_index;
584 return a == self.zcu.declPtr(b_decl_index).name;
585 }
586};
587
588/// The container that structs, enums, unions, and opaques have.
589pub const Namespace = struct {
590 parent: OptionalIndex,
591 file_scope: *File,
592 /// Will be a struct, enum, union, or opaque.
593 decl_index: Decl.Index,
594 /// Direct children of the namespace.
595 /// Declaration order is preserved via entry order.
596 /// These are only declarations named directly by the AST; anonymous
597 /// declarations are not stored here.
598 decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{},
599 /// Key is usingnamespace Decl itself. To find the namespace being included,
600 /// the Decl Value has to be resolved as a Type which has a Namespace.
601 /// Value is whether the usingnamespace decl is marked `pub`.
602 usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{},
603
604 const Index = InternPool.NamespaceIndex;
605 const OptionalIndex = InternPool.OptionalNamespaceIndex;
606
607 const DeclContext = struct {
608 zcu: *Zcu,
609
610 pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 {
611 const decl = ctx.zcu.declPtr(decl_index);
612 return std.hash.uint32(@intFromEnum(decl.name));
613 }
614
615 pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool {
616 _ = b_index;
617 const a_decl = ctx.zcu.declPtr(a_decl_index);
618 const b_decl = ctx.zcu.declPtr(b_decl_index);
619 return a_decl.name == b_decl.name;
620 }
621 };
622
623 // This renders e.g. "std.fs.Dir.OpenOptions"
624 pub fn renderFullyQualifiedName(
625 ns: Namespace,
626 zcu: *Zcu,
627 name: InternPool.NullTerminatedString,
628 writer: anytype,
629 ) @TypeOf(writer).Error!void {
630 if (ns.parent.unwrap()) |parent| {
631 try zcu.namespacePtr(parent).renderFullyQualifiedName(
632 zcu,
633 zcu.declPtr(ns.decl_index).name,
634 writer,
635 );
636 } else {
637 try ns.file_scope.renderFullyQualifiedName(writer);
638 }
639 if (name != .empty) try writer.print(".{}", .{name.fmt(&zcu.intern_pool)});
640 }
641
642 /// This renders e.g. "std/fs.zig:Dir.OpenOptions"
643 pub fn renderFullyQualifiedDebugName(
644 ns: Namespace,
645 zcu: *Zcu,
646 name: InternPool.NullTerminatedString,
647 writer: anytype,
648 ) @TypeOf(writer).Error!void {
649 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: {
650 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName(
651 zcu,
652 zcu.declPtr(ns.decl_index).name,
653 writer,
654 );
655 break :sep '.';
656 } else sep: {
657 try ns.file_scope.renderFullyQualifiedDebugName(writer);
658 break :sep ':';
659 };
660 if (name != .empty) try writer.print("{c}{}", .{ sep, name.fmt(&zcu.intern_pool) });
661 }
662
663 pub fn fullyQualifiedName(
664 ns: Namespace,
665 zcu: *Zcu,
666 name: InternPool.NullTerminatedString,
667 ) !InternPool.NullTerminatedString {
668 const ip = &zcu.intern_pool;
669 const count = count: {
670 var count: usize = name.length(ip) + 1;
671 var cur_ns = &ns;
672 while (true) {
673 const decl = zcu.declPtr(cur_ns.decl_index);
674 count += decl.name.length(ip) + 1;
675 cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse {
676 count += ns.file_scope.sub_file_path.len;
677 break :count count;
678 });
679 }
680 };
681
682 const gpa = zcu.gpa;
683 const start = ip.string_bytes.items.len;
684 // Protects reads of interned strings from being reallocated during the call to
685 // renderFullyQualifiedName.
686 try ip.string_bytes.ensureUnusedCapacity(gpa, count);
687 ns.renderFullyQualifiedName(zcu, name, ip.string_bytes.writer(gpa)) catch unreachable;
688
689 // Sanitize the name for nvptx which is more restrictive.
690 // TODO This should be handled by the backend, not the frontend. Have a
691 // look at how the C backend does it for inspiration.
692 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;
693 if (cpu_arch.isNvptx()) {
694 for (ip.string_bytes.items[start..]) |*byte| switch (byte.*) {
695 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_',
696 else => {},
697 };
698 }
699
700 return ip.getOrPutTrailingString(gpa, ip.string_bytes.items.len - start, .no_embedded_nulls);
701 }
702
703 pub fn getType(ns: Namespace, zcu: *Zcu) Type {
704 const decl = zcu.declPtr(ns.decl_index);
705 assert(decl.has_tv);
706 return decl.val.toType();
707 }
708};
709
710pub const File = struct {
711 /// The Decl of the struct that represents this File.
712 root_decl: Decl.OptionalIndex,
713 status: enum {
714 never_loaded,
715 retryable_failure,
716 parse_failure,
717 astgen_failure,
718 success_zir,
719 },
720 source_loaded: bool,
721 tree_loaded: bool,
722 zir_loaded: bool,
723 /// Relative to the owning package's root_src_dir.
724 /// Memory is stored in gpa, owned by File.
725 sub_file_path: []const u8,
726 /// Whether this is populated depends on `source_loaded`.
727 source: [:0]const u8,
728 /// Whether this is populated depends on `status`.
729 stat: Cache.File.Stat,
730 /// Whether this is populated or not depends on `tree_loaded`.
731 tree: Ast,
732 /// Whether this is populated or not depends on `zir_loaded`.
733 zir: Zir,
734 /// Module that this file is a part of, managed externally.
735 mod: *Package.Module,
736 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
737 multi_pkg: bool = false,
738 /// List of references to this file, used for multi-package errors.
739 references: std.ArrayListUnmanaged(Reference) = .{},
740 /// The hash of the path to this file, used to store `InternPool.TrackedInst`.
741 path_digest: Cache.BinDigest,
742
743 /// The most recent successful ZIR for this file, with no errors.
744 /// This is only populated when a previously successful ZIR
745 /// newly introduces compile errors during an update. When ZIR is
746 /// successful, this field is unloaded.
747 prev_zir: ?*Zir = null,
748
749 /// A single reference to a file.
750 pub const Reference = union(enum) {
751 /// The file is imported directly (i.e. not as a package) with @import.
752 import: SrcLoc,
753 /// The file is the root of a module.
754 root: *Package.Module,
755 };
756
757 pub fn unload(file: *File, gpa: Allocator) void {
758 file.unloadTree(gpa);
759 file.unloadSource(gpa);
760 file.unloadZir(gpa);
761 }
762
763 pub fn unloadTree(file: *File, gpa: Allocator) void {
764 if (file.tree_loaded) {
765 file.tree_loaded = false;
766 file.tree.deinit(gpa);
767 }
768 }
769
770 pub fn unloadSource(file: *File, gpa: Allocator) void {
771 if (file.source_loaded) {
772 file.source_loaded = false;
773 gpa.free(file.source);
774 }
775 }
776
777 pub fn unloadZir(file: *File, gpa: Allocator) void {
778 if (file.zir_loaded) {
779 file.zir_loaded = false;
780 file.zir.deinit(gpa);
781 }
782 }
783
784 pub fn deinit(file: *File, mod: *Module) void {
785 const gpa = mod.gpa;
786 const is_builtin = file.mod.isBuiltin();
787 log.debug("deinit File {s}", .{file.sub_file_path});
788 if (is_builtin) {
789 file.unloadTree(gpa);
790 file.unloadZir(gpa);
791 } else {
792 gpa.free(file.sub_file_path);
793 file.unload(gpa);
794 }
795 file.references.deinit(gpa);
796 if (file.root_decl.unwrap()) |root_decl| {
797 mod.destroyDecl(root_decl);
798 }
799 if (file.prev_zir) |prev_zir| {
800 prev_zir.deinit(gpa);
801 gpa.destroy(prev_zir);
802 }
803 file.* = undefined;
804 }
805
806 pub const Source = struct {
807 bytes: [:0]const u8,
808 stat: Cache.File.Stat,
809 };
810
811 pub fn getSource(file: *File, gpa: Allocator) !Source {
812 if (file.source_loaded) return Source{
813 .bytes = file.source,
814 .stat = file.stat,
815 };
816
817 // Keep track of inode, file size, mtime, hash so we can detect which files
818 // have been modified when an incremental update is requested.
819 var f = try file.mod.root.openFile(file.sub_file_path, .{});
820 defer f.close();
821
822 const stat = try f.stat();
823
824 if (stat.size > std.math.maxInt(u32))
825 return error.FileTooBig;
826
827 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
828 defer if (!file.source_loaded) gpa.free(source);
829 const amt = try f.readAll(source);
830 if (amt != stat.size)
831 return error.UnexpectedEndOfFile;
832
833 // Here we do not modify stat fields because this function is the one
834 // used for error reporting. We need to keep the stat fields stale so that
835 // astGenFile can know to regenerate ZIR.
836
837 file.source = source;
838 file.source_loaded = true;
839 return Source{
840 .bytes = source,
841 .stat = .{
842 .size = stat.size,
843 .inode = stat.inode,
844 .mtime = stat.mtime,
845 },
846 };
847 }
848
849 pub fn getTree(file: *File, gpa: Allocator) !*const Ast {
850 if (file.tree_loaded) return &file.tree;
851
852 const source = try file.getSource(gpa);
853 file.tree = try Ast.parse(gpa, source.bytes, .zig);
854 file.tree_loaded = true;
855 return &file.tree;
856 }
857
858 pub fn destroy(file: *File, mod: *Module) void {
859 const gpa = mod.gpa;
860 const is_builtin = file.mod.isBuiltin();
861 file.deinit(mod);
862 if (!is_builtin) gpa.destroy(file);
863 }
864
865 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {
866 // Convert all the slashes into dots and truncate the extension.
867 const ext = std.fs.path.extension(file.sub_file_path);
868 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
869 for (noext) |byte| switch (byte) {
870 '/', '\\' => try writer.writeByte('.'),
871 else => try writer.writeByte(byte),
872 };
873 }
874
875 pub fn renderFullyQualifiedDebugName(file: File, writer: anytype) !void {
876 for (file.sub_file_path) |byte| switch (byte) {
877 '/', '\\' => try writer.writeByte('/'),
878 else => try writer.writeByte(byte),
879 };
880 }
881
882 pub fn fullyQualifiedName(file: File, mod: *Module) !InternPool.NullTerminatedString {
883 const ip = &mod.intern_pool;
884 const start = ip.string_bytes.items.len;
885 try file.renderFullyQualifiedName(ip.string_bytes.writer(mod.gpa));
886 return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start, .no_embedded_nulls);
887 }
888
889 pub fn fullPath(file: File, ally: Allocator) ![]u8 {
890 return file.mod.root.joinString(ally, file.sub_file_path);
891 }
892
893 pub fn dumpSrc(file: *File, src: LazySrcLoc) void {
894 const loc = std.zig.findLineColumn(file.source.bytes, src);
895 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 });
896 }
897
898 pub fn okToReportErrors(file: File) bool {
899 return switch (file.status) {
900 .parse_failure, .astgen_failure => false,
901 else => true,
902 };
903 }
904
905 /// Add a reference to this file during AstGen.
906 pub fn addReference(file: *File, mod: Module, ref: Reference) !void {
907 // Don't add the same module root twice. Note that since we always add module roots at the
908 // front of the references array (see below), this loop is actually O(1) on valid code.
909 if (ref == .root) {
910 for (file.references.items) |other| {
911 switch (other) {
912 .root => |r| if (ref.root == r) return,
913 else => break, // reached the end of the "is-root" references
914 }
915 }
916 }
917
918 switch (ref) {
919 // We put root references at the front of the list both to make the above loop fast and
920 // to make multi-module errors more helpful (since "root-of" notes are generally more
921 // informative than "imported-from" notes). This path is hit very rarely, so the speed
922 // of the insert operation doesn't matter too much.
923 .root => try file.references.insert(mod.gpa, 0, ref),
924
925 // Other references we'll just put at the end.
926 else => try file.references.append(mod.gpa, ref),
927 }
928
929 const pkg = switch (ref) {
930 .import => |loc| loc.file_scope.mod,
931 .root => |pkg| pkg,
932 };
933 if (pkg != file.mod) file.multi_pkg = true;
934 }
935
936 /// Mark this file and every file referenced by it as multi_pkg and report an
937 /// astgen_failure error for them. AstGen must have completed in its entirety.
938 pub fn recursiveMarkMultiPkg(file: *File, mod: *Module) void {
939 file.multi_pkg = true;
940 file.status = .astgen_failure;
941
942 // We can only mark children as failed if the ZIR is loaded, which may not
943 // be the case if there were other astgen failures in this file
944 if (!file.zir_loaded) return;
945
946 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)];
947 if (imports_index == 0) return;
948 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
949
950 var extra_index = extra.end;
951 for (0..extra.data.imports_len) |_| {
952 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
953 extra_index = item.end;
954
955 const import_path = file.zir.nullTerminatedString(item.data.name);
956 if (mem.eql(u8, import_path, "builtin")) continue;
957
958 const res = mod.importFile(file, import_path) catch continue;
959 if (!res.is_pkg and !res.file.multi_pkg) {
960 res.file.recursiveMarkMultiPkg(mod);
961 }
962 }
963 }
964};
965
966pub const EmbedFile = struct {
967 /// Relative to the owning module's root directory.
968 sub_file_path: InternPool.NullTerminatedString,
969 /// Module that this file is a part of, managed externally.
970 owner: *Package.Module,
971 stat: Cache.File.Stat,
972 val: InternPool.Index,
973 src_loc: SrcLoc,
974};
975
976/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
977/// Its memory is managed with the general purpose allocator so that they
978/// can be created and destroyed in response to incremental updates.
979/// In some cases, the File could have been inferred from where the ErrorMsg
980/// is stored. For example, if it is stored in Module.failed_decls, then the File
981/// would be determined by the Decl Scope. However, the data structure contains the field
982/// anyway so that `ErrorMsg` can be reused for error notes, which may be in a different
983/// file than the parent error message. It also simplifies processing of error messages.
984pub const ErrorMsg = struct {
985 src_loc: SrcLoc,
986 msg: []const u8,
987 notes: []ErrorMsg = &.{},
988 reference_trace: []Trace = &.{},
989 hidden_references: u32 = 0,
990
991 pub const Trace = struct {
992 decl: InternPool.NullTerminatedString,
993 src_loc: SrcLoc,
994 };
995
996 pub fn create(
997 gpa: Allocator,
998 src_loc: SrcLoc,
999 comptime format: []const u8,
1000 args: anytype,
1001 ) !*ErrorMsg {
1002 assert(src_loc.lazy != .unneeded);
1003 const err_msg = try gpa.create(ErrorMsg);
1004 errdefer gpa.destroy(err_msg);
1005 err_msg.* = try ErrorMsg.init(gpa, src_loc, format, args);
1006 return err_msg;
1007 }
1008
1009 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`,
1010 /// as well as all notes.
1011 pub fn destroy(err_msg: *ErrorMsg, gpa: Allocator) void {
1012 err_msg.deinit(gpa);
1013 gpa.destroy(err_msg);
1014 }
1015
1016 pub fn init(
1017 gpa: Allocator,
1018 src_loc: SrcLoc,
1019 comptime format: []const u8,
1020 args: anytype,
1021 ) !ErrorMsg {
1022 return ErrorMsg{
1023 .src_loc = src_loc,
1024 .msg = try std.fmt.allocPrint(gpa, format, args),
1025 };
1026 }
1027
1028 pub fn deinit(err_msg: *ErrorMsg, gpa: Allocator) void {
1029 for (err_msg.notes) |*note| {
1030 note.deinit(gpa);
1031 }
1032 gpa.free(err_msg.notes);
1033 gpa.free(err_msg.msg);
1034 gpa.free(err_msg.reference_trace);
1035 err_msg.* = undefined;
1036 }
1037};
1038
1039/// Canonical reference to a position within a source file.
1040pub const SrcLoc = struct {
1041 file_scope: *File,
1042 base_node: Ast.Node.Index,
1043 /// Relative to `base_node`.
1044 lazy: LazySrcLoc.Offset,
1045
1046 pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex {
1047 const tree = src_loc.file_scope.tree;
1048 return tree.firstToken(src_loc.base_node);
1049 }
1050
1051 pub fn relativeToNodeIndex(src_loc: SrcLoc, offset: i32) Ast.Node.Index {
1052 return @bitCast(offset + @as(i32, @bitCast(src_loc.base_node)));
1053 }
1054
1055 pub const Span = Ast.Span;
1056
1057 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span {
1058 switch (src_loc.lazy) {
1059 .unneeded => unreachable,
1060 .entire_file => return Span{ .start = 0, .end = 1, .main = 0 },
1061
1062 .byte_abs => |byte_index| return Span{ .start = byte_index, .end = byte_index + 1, .main = byte_index },
1063
1064 .token_abs => |tok_index| {
1065 const tree = try src_loc.file_scope.getTree(gpa);
1066 const start = tree.tokens.items(.start)[tok_index];
1067 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1068 return Span{ .start = start, .end = end, .main = start };
1069 },
1070 .node_abs => |node| {
1071 const tree = try src_loc.file_scope.getTree(gpa);
1072 return tree.nodeToSpan(node);
1073 },
1074 .byte_offset => |byte_off| {
1075 const tree = try src_loc.file_scope.getTree(gpa);
1076 const tok_index = src_loc.baseSrcToken();
1077 const start = tree.tokens.items(.start)[tok_index] + byte_off;
1078 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1079 return Span{ .start = start, .end = end, .main = start };
1080 },
1081 .token_offset => |tok_off| {
1082 const tree = try src_loc.file_scope.getTree(gpa);
1083 const tok_index = src_loc.baseSrcToken() + tok_off;
1084 const start = tree.tokens.items(.start)[tok_index];
1085 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1086 return Span{ .start = start, .end = end, .main = start };
1087 },
1088 .node_offset => |traced_off| {
1089 const node_off = traced_off.x;
1090 const tree = try src_loc.file_scope.getTree(gpa);
1091 const node = src_loc.relativeToNodeIndex(node_off);
1092 assert(src_loc.file_scope.tree_loaded);
1093 return tree.nodeToSpan(node);
1094 },
1095 .node_offset_main_token => |node_off| {
1096 const tree = try src_loc.file_scope.getTree(gpa);
1097 const node = src_loc.relativeToNodeIndex(node_off);
1098 const main_token = tree.nodes.items(.main_token)[node];
1099 return tree.tokensToSpan(main_token, main_token, main_token);
1100 },
1101 .node_offset_bin_op => |node_off| {
1102 const tree = try src_loc.file_scope.getTree(gpa);
1103 const node = src_loc.relativeToNodeIndex(node_off);
1104 assert(src_loc.file_scope.tree_loaded);
1105 return tree.nodeToSpan(node);
1106 },
1107 .node_offset_initializer => |node_off| {
1108 const tree = try src_loc.file_scope.getTree(gpa);
1109 const node = src_loc.relativeToNodeIndex(node_off);
1110 return tree.tokensToSpan(
1111 tree.firstToken(node) - 3,
1112 tree.lastToken(node),
1113 tree.nodes.items(.main_token)[node] - 2,
1114 );
1115 },
1116 .node_offset_var_decl_ty => |node_off| {
1117 const tree = try src_loc.file_scope.getTree(gpa);
1118 const node = src_loc.relativeToNodeIndex(node_off);
1119 const node_tags = tree.nodes.items(.tag);
1120 const full = switch (node_tags[node]) {
1121 .global_var_decl,
1122 .local_var_decl,
1123 .simple_var_decl,
1124 .aligned_var_decl,
1125 => tree.fullVarDecl(node).?,
1126 .@"usingnamespace" => {
1127 const node_data = tree.nodes.items(.data);
1128 return tree.nodeToSpan(node_data[node].lhs);
1129 },
1130 else => unreachable,
1131 };
1132 if (full.ast.type_node != 0) {
1133 return tree.nodeToSpan(full.ast.type_node);
1134 }
1135 const tok_index = full.ast.mut_token + 1; // the name token
1136 const start = tree.tokens.items(.start)[tok_index];
1137 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1138 return Span{ .start = start, .end = end, .main = start };
1139 },
1140 .node_offset_var_decl_align => |node_off| {
1141 const tree = try src_loc.file_scope.getTree(gpa);
1142 const node = src_loc.relativeToNodeIndex(node_off);
1143 const full = tree.fullVarDecl(node).?;
1144 return tree.nodeToSpan(full.ast.align_node);
1145 },
1146 .node_offset_var_decl_section => |node_off| {
1147 const tree = try src_loc.file_scope.getTree(gpa);
1148 const node = src_loc.relativeToNodeIndex(node_off);
1149 const full = tree.fullVarDecl(node).?;
1150 return tree.nodeToSpan(full.ast.section_node);
1151 },
1152 .node_offset_var_decl_addrspace => |node_off| {
1153 const tree = try src_loc.file_scope.getTree(gpa);
1154 const node = src_loc.relativeToNodeIndex(node_off);
1155 const full = tree.fullVarDecl(node).?;
1156 return tree.nodeToSpan(full.ast.addrspace_node);
1157 },
1158 .node_offset_var_decl_init => |node_off| {
1159 const tree = try src_loc.file_scope.getTree(gpa);
1160 const node = src_loc.relativeToNodeIndex(node_off);
1161 const full = tree.fullVarDecl(node).?;
1162 return tree.nodeToSpan(full.ast.init_node);
1163 },
1164 .node_offset_builtin_call_arg => |builtin_arg| {
1165 const tree = try src_loc.file_scope.getTree(gpa);
1166 const node_datas = tree.nodes.items(.data);
1167 const node_tags = tree.nodes.items(.tag);
1168 const node = src_loc.relativeToNodeIndex(builtin_arg.builtin_call_node);
1169 const param = switch (node_tags[node]) {
1170 .builtin_call_two, .builtin_call_two_comma => switch (builtin_arg.arg_index) {
1171 0 => node_datas[node].lhs,
1172 1 => node_datas[node].rhs,
1173 else => unreachable,
1174 },
1175 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + builtin_arg.arg_index],
1176 else => unreachable,
1177 };
1178 return tree.nodeToSpan(param);
1179 },
1180 .node_offset_ptrcast_operand => |node_off| {
1181 const tree = try src_loc.file_scope.getTree(gpa);
1182 const main_tokens = tree.nodes.items(.main_token);
1183 const node_datas = tree.nodes.items(.data);
1184 const node_tags = tree.nodes.items(.tag);
1185
1186 var node = src_loc.relativeToNodeIndex(node_off);
1187 while (true) {
1188 switch (node_tags[node]) {
1189 .builtin_call_two, .builtin_call_two_comma => {},
1190 else => break,
1191 }
1192
1193 if (node_datas[node].lhs == 0) break; // 0 args
1194 if (node_datas[node].rhs != 0) break; // 2 args
1195
1196 const builtin_token = main_tokens[node];
1197 const builtin_name = tree.tokenSlice(builtin_token);
1198 const info = BuiltinFn.list.get(builtin_name) orelse break;
1199
1200 switch (info.tag) {
1201 else => break,
1202 .ptr_cast,
1203 .align_cast,
1204 .addrspace_cast,
1205 .const_cast,
1206 .volatile_cast,
1207 => {},
1208 }
1209
1210 node = node_datas[node].lhs;
1211 }
1212
1213 return tree.nodeToSpan(node);
1214 },
1215 .node_offset_array_access_index => |node_off| {
1216 const tree = try src_loc.file_scope.getTree(gpa);
1217 const node_datas = tree.nodes.items(.data);
1218 const node = src_loc.relativeToNodeIndex(node_off);
1219 return tree.nodeToSpan(node_datas[node].rhs);
1220 },
1221 .node_offset_slice_ptr,
1222 .node_offset_slice_start,
1223 .node_offset_slice_end,
1224 .node_offset_slice_sentinel,
1225 => |node_off| {
1226 const tree = try src_loc.file_scope.getTree(gpa);
1227 const node = src_loc.relativeToNodeIndex(node_off);
1228 const full = tree.fullSlice(node).?;
1229 const part_node = switch (src_loc.lazy) {
1230 .node_offset_slice_ptr => full.ast.sliced,
1231 .node_offset_slice_start => full.ast.start,
1232 .node_offset_slice_end => full.ast.end,
1233 .node_offset_slice_sentinel => full.ast.sentinel,
1234 else => unreachable,
1235 };
1236 return tree.nodeToSpan(part_node);
1237 },
1238 .node_offset_call_func => |node_off| {
1239 const tree = try src_loc.file_scope.getTree(gpa);
1240 const node = src_loc.relativeToNodeIndex(node_off);
1241 var buf: [1]Ast.Node.Index = undefined;
1242 const full = tree.fullCall(&buf, node).?;
1243 return tree.nodeToSpan(full.ast.fn_expr);
1244 },
1245 .node_offset_field_name => |node_off| {
1246 const tree = try src_loc.file_scope.getTree(gpa);
1247 const node_datas = tree.nodes.items(.data);
1248 const node_tags = tree.nodes.items(.tag);
1249 const node = src_loc.relativeToNodeIndex(node_off);
1250 var buf: [1]Ast.Node.Index = undefined;
1251 const tok_index = switch (node_tags[node]) {
1252 .field_access => node_datas[node].rhs,
1253 .call_one,
1254 .call_one_comma,
1255 .async_call_one,
1256 .async_call_one_comma,
1257 .call,
1258 .call_comma,
1259 .async_call,
1260 .async_call_comma,
1261 => blk: {
1262 const full = tree.fullCall(&buf, node).?;
1263 break :blk tree.lastToken(full.ast.fn_expr);
1264 },
1265 else => tree.firstToken(node) - 2,
1266 };
1267 const start = tree.tokens.items(.start)[tok_index];
1268 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1269 return Span{ .start = start, .end = end, .main = start };
1270 },
1271 .node_offset_field_name_init => |node_off| {
1272 const tree = try src_loc.file_scope.getTree(gpa);
1273 const node = src_loc.relativeToNodeIndex(node_off);
1274 const tok_index = tree.firstToken(node) - 2;
1275 const start = tree.tokens.items(.start)[tok_index];
1276 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1277 return Span{ .start = start, .end = end, .main = start };
1278 },
1279 .node_offset_deref_ptr => |node_off| {
1280 const tree = try src_loc.file_scope.getTree(gpa);
1281 const node = src_loc.relativeToNodeIndex(node_off);
1282 return tree.nodeToSpan(node);
1283 },
1284 .node_offset_asm_source => |node_off| {
1285 const tree = try src_loc.file_scope.getTree(gpa);
1286 const node = src_loc.relativeToNodeIndex(node_off);
1287 const full = tree.fullAsm(node).?;
1288 return tree.nodeToSpan(full.ast.template);
1289 },
1290 .node_offset_asm_ret_ty => |node_off| {
1291 const tree = try src_loc.file_scope.getTree(gpa);
1292 const node = src_loc.relativeToNodeIndex(node_off);
1293 const full = tree.fullAsm(node).?;
1294 const asm_output = full.outputs[0];
1295 const node_datas = tree.nodes.items(.data);
1296 return tree.nodeToSpan(node_datas[asm_output].lhs);
1297 },
1298
1299 .node_offset_if_cond => |node_off| {
1300 const tree = try src_loc.file_scope.getTree(gpa);
1301 const node = src_loc.relativeToNodeIndex(node_off);
1302 const node_tags = tree.nodes.items(.tag);
1303 const src_node = switch (node_tags[node]) {
1304 .if_simple,
1305 .@"if",
1306 => tree.fullIf(node).?.ast.cond_expr,
1307
1308 .while_simple,
1309 .while_cont,
1310 .@"while",
1311 => tree.fullWhile(node).?.ast.cond_expr,
1312
1313 .for_simple,
1314 .@"for",
1315 => {
1316 const inputs = tree.fullFor(node).?.ast.inputs;
1317 const start = tree.firstToken(inputs[0]);
1318 const end = tree.lastToken(inputs[inputs.len - 1]);
1319 return tree.tokensToSpan(start, end, start);
1320 },
1321
1322 .@"orelse" => node,
1323 .@"catch" => node,
1324 else => unreachable,
1325 };
1326 return tree.nodeToSpan(src_node);
1327 },
1328 .for_input => |for_input| {
1329 const tree = try src_loc.file_scope.getTree(gpa);
1330 const node = src_loc.relativeToNodeIndex(for_input.for_node_offset);
1331 const for_full = tree.fullFor(node).?;
1332 const src_node = for_full.ast.inputs[for_input.input_index];
1333 return tree.nodeToSpan(src_node);
1334 },
1335 .for_capture_from_input => |node_off| {
1336 const tree = try src_loc.file_scope.getTree(gpa);
1337 const token_tags = tree.tokens.items(.tag);
1338 const input_node = src_loc.relativeToNodeIndex(node_off);
1339 // We have to actually linear scan the whole AST to find the for loop
1340 // that contains this input.
1341 const node_tags = tree.nodes.items(.tag);
1342 for (node_tags, 0..) |node_tag, node_usize| {
1343 const node = @as(Ast.Node.Index, @intCast(node_usize));
1344 switch (node_tag) {
1345 .for_simple, .@"for" => {
1346 const for_full = tree.fullFor(node).?;
1347 for (for_full.ast.inputs, 0..) |input, input_index| {
1348 if (input_node == input) {
1349 var count = input_index;
1350 var tok = for_full.payload_token;
1351 while (true) {
1352 switch (token_tags[tok]) {
1353 .comma => {
1354 count -= 1;
1355 tok += 1;
1356 },
1357 .identifier => {
1358 if (count == 0)
1359 return tree.tokensToSpan(tok, tok + 1, tok);
1360 tok += 1;
1361 },
1362 .asterisk => {
1363 if (count == 0)
1364 return tree.tokensToSpan(tok, tok + 2, tok);
1365 tok += 1;
1366 },
1367 else => unreachable,
1368 }
1369 }
1370 }
1371 }
1372 },
1373 else => continue,
1374 }
1375 } else unreachable;
1376 },
1377 .call_arg => |call_arg| {
1378 const tree = try src_loc.file_scope.getTree(gpa);
1379 const node = src_loc.relativeToNodeIndex(call_arg.call_node_offset);
1380 var buf: [2]Ast.Node.Index = undefined;
1381 const call_full = tree.fullCall(buf[0..1], node) orelse {
1382 const node_tags = tree.nodes.items(.tag);
1383 assert(node_tags[node] == .builtin_call);
1384 const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1];
1385 switch (node_tags[call_args_node]) {
1386 .array_init_one,
1387 .array_init_one_comma,
1388 .array_init_dot_two,
1389 .array_init_dot_two_comma,
1390 .array_init_dot,
1391 .array_init_dot_comma,
1392 .array_init,
1393 .array_init_comma,
1394 => {
1395 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements;
1396 return tree.nodeToSpan(full[call_arg.arg_index]);
1397 },
1398 .struct_init_one,
1399 .struct_init_one_comma,
1400 .struct_init_dot_two,
1401 .struct_init_dot_two_comma,
1402 .struct_init_dot,
1403 .struct_init_dot_comma,
1404 .struct_init,
1405 .struct_init_comma,
1406 => {
1407 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields;
1408 return tree.nodeToSpan(full[call_arg.arg_index]);
1409 },
1410 else => return tree.nodeToSpan(call_args_node),
1411 }
1412 };
1413 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]);
1414 },
1415 .fn_proto_param, .fn_proto_param_type => |fn_proto_param| {
1416 const tree = try src_loc.file_scope.getTree(gpa);
1417 const node = src_loc.relativeToNodeIndex(fn_proto_param.fn_proto_node_offset);
1418 var buf: [1]Ast.Node.Index = undefined;
1419 const full = tree.fullFnProto(&buf, node).?;
1420 var it = full.iterate(tree);
1421 var i: usize = 0;
1422 while (it.next()) |param| : (i += 1) {
1423 if (i != fn_proto_param.param_index) continue;
1424
1425 switch (src_loc.lazy) {
1426 .fn_proto_param_type => if (param.anytype_ellipsis3) |tok| {
1427 return tree.tokenToSpan(tok);
1428 } else {
1429 return tree.nodeToSpan(param.type_expr);
1430 },
1431 .fn_proto_param => if (param.anytype_ellipsis3) |tok| {
1432 const first = param.comptime_noalias orelse param.name_token orelse tok;
1433 return tree.tokensToSpan(first, tok, first);
1434 } else {
1435 const first = param.comptime_noalias orelse param.name_token orelse tree.firstToken(param.type_expr);
1436 return tree.tokensToSpan(first, tree.lastToken(param.type_expr), first);
1437 },
1438 else => unreachable,
1439 }
1440 }
1441 unreachable;
1442 },
1443 .node_offset_bin_lhs => |node_off| {
1444 const tree = try src_loc.file_scope.getTree(gpa);
1445 const node = src_loc.relativeToNodeIndex(node_off);
1446 const node_datas = tree.nodes.items(.data);
1447 return tree.nodeToSpan(node_datas[node].lhs);
1448 },
1449 .node_offset_bin_rhs => |node_off| {
1450 const tree = try src_loc.file_scope.getTree(gpa);
1451 const node = src_loc.relativeToNodeIndex(node_off);
1452 const node_datas = tree.nodes.items(.data);
1453 return tree.nodeToSpan(node_datas[node].rhs);
1454 },
1455 .array_cat_lhs, .array_cat_rhs => |cat| {
1456 const tree = try src_loc.file_scope.getTree(gpa);
1457 const node = src_loc.relativeToNodeIndex(cat.array_cat_offset);
1458 const node_datas = tree.nodes.items(.data);
1459 const arr_node = if (src_loc.lazy == .array_cat_lhs)
1460 node_datas[node].lhs
1461 else
1462 node_datas[node].rhs;
1463
1464 const node_tags = tree.nodes.items(.tag);
1465 var buf: [2]Ast.Node.Index = undefined;
1466 switch (node_tags[arr_node]) {
1467 .array_init_one,
1468 .array_init_one_comma,
1469 .array_init_dot_two,
1470 .array_init_dot_two_comma,
1471 .array_init_dot,
1472 .array_init_dot_comma,
1473 .array_init,
1474 .array_init_comma,
1475 => {
1476 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements;
1477 return tree.nodeToSpan(full[cat.elem_index]);
1478 },
1479 else => return tree.nodeToSpan(arr_node),
1480 }
1481 },
1482
1483 .node_offset_switch_operand => |node_off| {
1484 const tree = try src_loc.file_scope.getTree(gpa);
1485 const node = src_loc.relativeToNodeIndex(node_off);
1486 const node_datas = tree.nodes.items(.data);
1487 return tree.nodeToSpan(node_datas[node].lhs);
1488 },
1489
1490 .node_offset_switch_special_prong => |node_off| {
1491 const tree = try src_loc.file_scope.getTree(gpa);
1492 const switch_node = src_loc.relativeToNodeIndex(node_off);
1493 const node_datas = tree.nodes.items(.data);
1494 const node_tags = tree.nodes.items(.tag);
1495 const main_tokens = tree.nodes.items(.main_token);
1496 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
1497 const case_nodes = tree.extra_data[extra.start..extra.end];
1498 for (case_nodes) |case_node| {
1499 const case = tree.fullSwitchCase(case_node).?;
1500 const is_special = (case.ast.values.len == 0) or
1501 (case.ast.values.len == 1 and
1502 node_tags[case.ast.values[0]] == .identifier and
1503 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1504 if (!is_special) continue;
1505
1506 return tree.nodeToSpan(case_node);
1507 } else unreachable;
1508 },
1509
1510 .node_offset_switch_range => |node_off| {
1511 const tree = try src_loc.file_scope.getTree(gpa);
1512 const switch_node = src_loc.relativeToNodeIndex(node_off);
1513 const node_datas = tree.nodes.items(.data);
1514 const node_tags = tree.nodes.items(.tag);
1515 const main_tokens = tree.nodes.items(.main_token);
1516 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
1517 const case_nodes = tree.extra_data[extra.start..extra.end];
1518 for (case_nodes) |case_node| {
1519 const case = tree.fullSwitchCase(case_node).?;
1520 const is_special = (case.ast.values.len == 0) or
1521 (case.ast.values.len == 1 and
1522 node_tags[case.ast.values[0]] == .identifier and
1523 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1524 if (is_special) continue;
1525
1526 for (case.ast.values) |item_node| {
1527 if (node_tags[item_node] == .switch_range) {
1528 return tree.nodeToSpan(item_node);
1529 }
1530 }
1531 } else unreachable;
1532 },
1533 .node_offset_fn_type_align => |node_off| {
1534 const tree = try src_loc.file_scope.getTree(gpa);
1535 const node = src_loc.relativeToNodeIndex(node_off);
1536 var buf: [1]Ast.Node.Index = undefined;
1537 const full = tree.fullFnProto(&buf, node).?;
1538 return tree.nodeToSpan(full.ast.align_expr);
1539 },
1540 .node_offset_fn_type_addrspace => |node_off| {
1541 const tree = try src_loc.file_scope.getTree(gpa);
1542 const node = src_loc.relativeToNodeIndex(node_off);
1543 var buf: [1]Ast.Node.Index = undefined;
1544 const full = tree.fullFnProto(&buf, node).?;
1545 return tree.nodeToSpan(full.ast.addrspace_expr);
1546 },
1547 .node_offset_fn_type_section => |node_off| {
1548 const tree = try src_loc.file_scope.getTree(gpa);
1549 const node = src_loc.relativeToNodeIndex(node_off);
1550 var buf: [1]Ast.Node.Index = undefined;
1551 const full = tree.fullFnProto(&buf, node).?;
1552 return tree.nodeToSpan(full.ast.section_expr);
1553 },
1554 .node_offset_fn_type_cc => |node_off| {
1555 const tree = try src_loc.file_scope.getTree(gpa);
1556 const node = src_loc.relativeToNodeIndex(node_off);
1557 var buf: [1]Ast.Node.Index = undefined;
1558 const full = tree.fullFnProto(&buf, node).?;
1559 return tree.nodeToSpan(full.ast.callconv_expr);
1560 },
1561
1562 .node_offset_fn_type_ret_ty => |node_off| {
1563 const tree = try src_loc.file_scope.getTree(gpa);
1564 const node = src_loc.relativeToNodeIndex(node_off);
1565 var buf: [1]Ast.Node.Index = undefined;
1566 const full = tree.fullFnProto(&buf, node).?;
1567 return tree.nodeToSpan(full.ast.return_type);
1568 },
1569 .node_offset_param => |node_off| {
1570 const tree = try src_loc.file_scope.getTree(gpa);
1571 const token_tags = tree.tokens.items(.tag);
1572 const node = src_loc.relativeToNodeIndex(node_off);
1573
1574 var first_tok = tree.firstToken(node);
1575 while (true) switch (token_tags[first_tok - 1]) {
1576 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1577 else => break,
1578 };
1579 return tree.tokensToSpan(
1580 first_tok,
1581 tree.lastToken(node),
1582 first_tok,
1583 );
1584 },
1585 .token_offset_param => |token_off| {
1586 const tree = try src_loc.file_scope.getTree(gpa);
1587 const token_tags = tree.tokens.items(.tag);
1588 const main_token = tree.nodes.items(.main_token)[src_loc.base_node];
1589 const tok_index = @as(Ast.TokenIndex, @bitCast(token_off + @as(i32, @bitCast(main_token))));
1590
1591 var first_tok = tok_index;
1592 while (true) switch (token_tags[first_tok - 1]) {
1593 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1594 else => break,
1595 };
1596 return tree.tokensToSpan(
1597 first_tok,
1598 tok_index,
1599 first_tok,
1600 );
1601 },
1602
1603 .node_offset_anyframe_type => |node_off| {
1604 const tree = try src_loc.file_scope.getTree(gpa);
1605 const node_datas = tree.nodes.items(.data);
1606 const parent_node = src_loc.relativeToNodeIndex(node_off);
1607 return tree.nodeToSpan(node_datas[parent_node].rhs);
1608 },
1609
1610 .node_offset_lib_name => |node_off| {
1611 const tree = try src_loc.file_scope.getTree(gpa);
1612 const parent_node = src_loc.relativeToNodeIndex(node_off);
1613 var buf: [1]Ast.Node.Index = undefined;
1614 const full = tree.fullFnProto(&buf, parent_node).?;
1615 const tok_index = full.lib_name.?;
1616 const start = tree.tokens.items(.start)[tok_index];
1617 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len));
1618 return Span{ .start = start, .end = end, .main = start };
1619 },
1620
1621 .node_offset_array_type_len => |node_off| {
1622 const tree = try src_loc.file_scope.getTree(gpa);
1623 const parent_node = src_loc.relativeToNodeIndex(node_off);
1624
1625 const full = tree.fullArrayType(parent_node).?;
1626 return tree.nodeToSpan(full.ast.elem_count);
1627 },
1628 .node_offset_array_type_sentinel => |node_off| {
1629 const tree = try src_loc.file_scope.getTree(gpa);
1630 const parent_node = src_loc.relativeToNodeIndex(node_off);
1631
1632 const full = tree.fullArrayType(parent_node).?;
1633 return tree.nodeToSpan(full.ast.sentinel);
1634 },
1635 .node_offset_array_type_elem => |node_off| {
1636 const tree = try src_loc.file_scope.getTree(gpa);
1637 const parent_node = src_loc.relativeToNodeIndex(node_off);
1638
1639 const full = tree.fullArrayType(parent_node).?;
1640 return tree.nodeToSpan(full.ast.elem_type);
1641 },
1642 .node_offset_un_op => |node_off| {
1643 const tree = try src_loc.file_scope.getTree(gpa);
1644 const node_datas = tree.nodes.items(.data);
1645 const node = src_loc.relativeToNodeIndex(node_off);
1646
1647 return tree.nodeToSpan(node_datas[node].lhs);
1648 },
1649 .node_offset_ptr_elem => |node_off| {
1650 const tree = try src_loc.file_scope.getTree(gpa);
1651 const parent_node = src_loc.relativeToNodeIndex(node_off);
1652
1653 const full = tree.fullPtrType(parent_node).?;
1654 return tree.nodeToSpan(full.ast.child_type);
1655 },
1656 .node_offset_ptr_sentinel => |node_off| {
1657 const tree = try src_loc.file_scope.getTree(gpa);
1658 const parent_node = src_loc.relativeToNodeIndex(node_off);
1659
1660 const full = tree.fullPtrType(parent_node).?;
1661 return tree.nodeToSpan(full.ast.sentinel);
1662 },
1663 .node_offset_ptr_align => |node_off| {
1664 const tree = try src_loc.file_scope.getTree(gpa);
1665 const parent_node = src_loc.relativeToNodeIndex(node_off);
1666
1667 const full = tree.fullPtrType(parent_node).?;
1668 return tree.nodeToSpan(full.ast.align_node);
1669 },
1670 .node_offset_ptr_addrspace => |node_off| {
1671 const tree = try src_loc.file_scope.getTree(gpa);
1672 const parent_node = src_loc.relativeToNodeIndex(node_off);
1673
1674 const full = tree.fullPtrType(parent_node).?;
1675 return tree.nodeToSpan(full.ast.addrspace_node);
1676 },
1677 .node_offset_ptr_bitoffset => |node_off| {
1678 const tree = try src_loc.file_scope.getTree(gpa);
1679 const parent_node = src_loc.relativeToNodeIndex(node_off);
1680
1681 const full = tree.fullPtrType(parent_node).?;
1682 return tree.nodeToSpan(full.ast.bit_range_start);
1683 },
1684 .node_offset_ptr_hostsize => |node_off| {
1685 const tree = try src_loc.file_scope.getTree(gpa);
1686 const parent_node = src_loc.relativeToNodeIndex(node_off);
1687
1688 const full = tree.fullPtrType(parent_node).?;
1689 return tree.nodeToSpan(full.ast.bit_range_end);
1690 },
1691 .node_offset_container_tag => |node_off| {
1692 const tree = try src_loc.file_scope.getTree(gpa);
1693 const node_tags = tree.nodes.items(.tag);
1694 const parent_node = src_loc.relativeToNodeIndex(node_off);
1695
1696 switch (node_tags[parent_node]) {
1697 .container_decl_arg, .container_decl_arg_trailing => {
1698 const full = tree.containerDeclArg(parent_node);
1699 return tree.nodeToSpan(full.ast.arg);
1700 },
1701 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => {
1702 const full = tree.taggedUnionEnumTag(parent_node);
1703
1704 return tree.tokensToSpan(
1705 tree.firstToken(full.ast.arg) - 2,
1706 tree.lastToken(full.ast.arg) + 1,
1707 tree.nodes.items(.main_token)[full.ast.arg],
1708 );
1709 },
1710 else => unreachable,
1711 }
1712 },
1713 .node_offset_field_default => |node_off| {
1714 const tree = try src_loc.file_scope.getTree(gpa);
1715 const node_tags = tree.nodes.items(.tag);
1716 const parent_node = src_loc.relativeToNodeIndex(node_off);
1717
1718 const full: Ast.full.ContainerField = switch (node_tags[parent_node]) {
1719 .container_field => tree.containerField(parent_node),
1720 .container_field_init => tree.containerFieldInit(parent_node),
1721 else => unreachable,
1722 };
1723 return tree.nodeToSpan(full.ast.value_expr);
1724 },
1725 .node_offset_init_ty => |node_off| {
1726 const tree = try src_loc.file_scope.getTree(gpa);
1727 const parent_node = src_loc.relativeToNodeIndex(node_off);
1728
1729 var buf: [2]Ast.Node.Index = undefined;
1730 const type_expr = if (tree.fullArrayInit(&buf, parent_node)) |array_init|
1731 array_init.ast.type_expr
1732 else
1733 tree.fullStructInit(&buf, parent_node).?.ast.type_expr;
1734 return tree.nodeToSpan(type_expr);
1735 },
1736 .node_offset_store_ptr => |node_off| {
1737 const tree = try src_loc.file_scope.getTree(gpa);
1738 const node_tags = tree.nodes.items(.tag);
1739 const node_datas = tree.nodes.items(.data);
1740 const node = src_loc.relativeToNodeIndex(node_off);
1741
1742 switch (node_tags[node]) {
1743 .assign => {
1744 return tree.nodeToSpan(node_datas[node].lhs);
1745 },
1746 else => return tree.nodeToSpan(node),
1747 }
1748 },
1749 .node_offset_store_operand => |node_off| {
1750 const tree = try src_loc.file_scope.getTree(gpa);
1751 const node_tags = tree.nodes.items(.tag);
1752 const node_datas = tree.nodes.items(.data);
1753 const node = src_loc.relativeToNodeIndex(node_off);
1754
1755 switch (node_tags[node]) {
1756 .assign => {
1757 return tree.nodeToSpan(node_datas[node].rhs);
1758 },
1759 else => return tree.nodeToSpan(node),
1760 }
1761 },
1762 .node_offset_return_operand => |node_off| {
1763 const tree = try src_loc.file_scope.getTree(gpa);
1764 const node = src_loc.relativeToNodeIndex(node_off);
1765 const node_tags = tree.nodes.items(.tag);
1766 const node_datas = tree.nodes.items(.data);
1767 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) {
1768 return tree.nodeToSpan(node_datas[node].lhs);
1769 }
1770 return tree.nodeToSpan(node);
1771 },
1772 .container_field_name,
1773 .container_field_value,
1774 .container_field_type,
1775 .container_field_align,
1776 => |field_idx| {
1777 const tree = try src_loc.file_scope.getTree(gpa);
1778 const node = src_loc.relativeToNodeIndex(0);
1779 var buf: [2]Ast.Node.Index = undefined;
1780 const container_decl = tree.fullContainerDecl(&buf, node) orelse
1781 return tree.nodeToSpan(node);
1782
1783 var cur_field_idx: usize = 0;
1784 for (container_decl.ast.members) |member_node| {
1785 const field = tree.fullContainerField(member_node) orelse continue;
1786 if (cur_field_idx < field_idx) {
1787 cur_field_idx += 1;
1788 continue;
1789 }
1790 const field_component_node = switch (src_loc.lazy) {
1791 .container_field_name => 0,
1792 .container_field_value => field.ast.value_expr,
1793 .container_field_type => field.ast.type_expr,
1794 .container_field_align => field.ast.align_expr,
1795 else => unreachable,
1796 };
1797 if (field_component_node == 0) {
1798 return tree.tokenToSpan(field.ast.main_token);
1799 } else {
1800 return tree.nodeToSpan(field_component_node);
1801 }
1802 } else unreachable;
1803 },
1804 .init_elem => |init_elem| {
1805 const tree = try src_loc.file_scope.getTree(gpa);
1806 const init_node = src_loc.relativeToNodeIndex(init_elem.init_node_offset);
1807 var buf: [2]Ast.Node.Index = undefined;
1808 if (tree.fullArrayInit(&buf, init_node)) |full| {
1809 const elem_node = full.ast.elements[init_elem.elem_index];
1810 return tree.nodeToSpan(elem_node);
1811 } else if (tree.fullStructInit(&buf, init_node)) |full| {
1812 const field_node = full.ast.fields[init_elem.elem_index];
1813 return tree.tokensToSpan(
1814 tree.firstToken(field_node) - 3,
1815 tree.lastToken(field_node),
1816 tree.nodes.items(.main_token)[field_node] - 2,
1817 );
1818 } else unreachable;
1819 },
1820 .init_field_name,
1821 .init_field_linkage,
1822 .init_field_section,
1823 .init_field_visibility,
1824 .init_field_rw,
1825 .init_field_locality,
1826 .init_field_cache,
1827 .init_field_library,
1828 .init_field_thread_local,
1829 => |builtin_call_node| {
1830 const wanted = switch (src_loc.lazy) {
1831 .init_field_name => "name",
1832 .init_field_linkage => "linkage",
1833 .init_field_section => "section",
1834 .init_field_visibility => "visibility",
1835 .init_field_rw => "rw",
1836 .init_field_locality => "locality",
1837 .init_field_cache => "cache",
1838 .init_field_library => "library",
1839 .init_field_thread_local => "thread_local",
1840 else => unreachable,
1841 };
1842 const tree = try src_loc.file_scope.getTree(gpa);
1843 const node_datas = tree.nodes.items(.data);
1844 const node_tags = tree.nodes.items(.tag);
1845 const node = src_loc.relativeToNodeIndex(builtin_call_node);
1846 const arg_node = switch (node_tags[node]) {
1847 .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs,
1848 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1],
1849 else => unreachable,
1850 };
1851 var buf: [2]Ast.Node.Index = undefined;
1852 const full = tree.fullStructInit(&buf, arg_node) orelse
1853 return tree.nodeToSpan(arg_node);
1854 for (full.ast.fields) |field_node| {
1855 // . IDENTIFIER = field_node
1856 const name_token = tree.firstToken(field_node) - 2;
1857 const name = tree.tokenSlice(name_token);
1858 if (std.mem.eql(u8, name, wanted)) {
1859 return tree.tokensToSpan(
1860 name_token - 1,
1861 tree.lastToken(field_node),
1862 tree.nodes.items(.main_token)[field_node] - 2,
1863 );
1864 }
1865 }
1866 return tree.nodeToSpan(arg_node);
1867 },
1868 .switch_case_item,
1869 .switch_case_item_range_first,
1870 .switch_case_item_range_last,
1871 .switch_capture,
1872 .switch_tag_capture,
1873 => {
1874 const switch_node_offset, const want_case_idx = switch (src_loc.lazy) {
1875 .switch_case_item,
1876 .switch_case_item_range_first,
1877 .switch_case_item_range_last,
1878 => |x| .{ x.switch_node_offset, x.case_idx },
1879 .switch_capture,
1880 .switch_tag_capture,
1881 => |x| .{ x.switch_node_offset, x.case_idx },
1882 else => unreachable,
1883 };
1884
1885 const tree = try src_loc.file_scope.getTree(gpa);
1886 const node_datas = tree.nodes.items(.data);
1887 const node_tags = tree.nodes.items(.tag);
1888 const main_tokens = tree.nodes.items(.main_token);
1889 const switch_node = src_loc.relativeToNodeIndex(switch_node_offset);
1890 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
1891 const case_nodes = tree.extra_data[extra.start..extra.end];
1892
1893 var multi_i: u32 = 0;
1894 var scalar_i: u32 = 0;
1895 const case = for (case_nodes) |case_node| {
1896 const case = tree.fullSwitchCase(case_node).?;
1897 const is_special = special: {
1898 if (case.ast.values.len == 0) break :special true;
1899 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .identifier) {
1900 break :special mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_");
1901 }
1902 break :special false;
1903 };
1904 if (is_special) {
1905 if (want_case_idx.isSpecial()) {
1906 break case;
1907 }
1908 }
1909
1910 const is_multi = case.ast.values.len != 1 or
1911 node_tags[case.ast.values[0]] == .switch_range;
1912
1913 if (!want_case_idx.isSpecial()) switch (want_case_idx.kind) {
1914 .scalar => if (!is_multi and want_case_idx.index == scalar_i) break case,
1915 .multi => if (is_multi and want_case_idx.index == multi_i) break case,
1916 };
1917
1918 if (is_multi) {
1919 multi_i += 1;
1920 } else {
1921 scalar_i += 1;
1922 }
1923 } else unreachable;
1924
1925 const want_item = switch (src_loc.lazy) {
1926 .switch_case_item,
1927 .switch_case_item_range_first,
1928 .switch_case_item_range_last,
1929 => |x| x.item_idx,
1930 .switch_capture, .switch_tag_capture => {
1931 const token_tags = tree.tokens.items(.tag);
1932 const start = switch (src_loc.lazy) {
1933 .switch_capture => case.payload_token.?,
1934 .switch_tag_capture => tok: {
1935 var tok = case.payload_token.?;
1936 if (token_tags[tok] == .asterisk) tok += 1;
1937 tok += 2; // skip over comma
1938 break :tok tok;
1939 },
1940 else => unreachable,
1941 };
1942 const end = switch (token_tags[start]) {
1943 .asterisk => start + 1,
1944 else => start,
1945 };
1946 return tree.tokensToSpan(start, end, start);
1947 },
1948 else => unreachable,
1949 };
1950
1951 switch (want_item.kind) {
1952 .single => {
1953 var item_i: u32 = 0;
1954 for (case.ast.values) |item_node| {
1955 if (node_tags[item_node] == .switch_range) continue;
1956 if (item_i != want_item.index) {
1957 item_i += 1;
1958 continue;
1959 }
1960 return tree.nodeToSpan(item_node);
1961 } else unreachable;
1962 },
1963 .range => {
1964 var range_i: u32 = 0;
1965 for (case.ast.values) |item_node| {
1966 if (node_tags[item_node] != .switch_range) continue;
1967 if (range_i != want_item.index) {
1968 range_i += 1;
1969 continue;
1970 }
1971 return switch (src_loc.lazy) {
1972 .switch_case_item => tree.nodeToSpan(item_node),
1973 .switch_case_item_range_first => tree.nodeToSpan(node_datas[item_node].lhs),
1974 .switch_case_item_range_last => tree.nodeToSpan(node_datas[item_node].rhs),
1975 else => unreachable,
1976 };
1977 } else unreachable;
1978 },
1979 }
1980 },
1981 }
1982 }
1983};
1984
1985pub const LazySrcLoc = struct {
1986 /// This instruction provides the source node locations are resolved relative to.
1987 /// It is a `declaration`, `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl`.
1988 /// This must be valid even if `relative` is an absolute value, since it is required to
1989 /// determine the file which the `LazySrcLoc` refers to.
1990 base_node_inst: InternPool.TrackedInst.Index,
1991 /// This field determines the source location relative to `base_node_inst`.
1992 offset: Offset,
1993
1994 pub const Offset = union(enum) {
1995 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
1996 /// that all code paths which would need to resolve the source location are
1997 /// unreachable. If you are debugging this tag incorrectly being this value,
1998 /// look into using reverse-continue with a memory watchpoint to see where the
1999 /// value is being set to this tag.
2000 /// `base_node_inst` is unused.
2001 unneeded,
2002 /// Means the source location points to an entire file; not any particular
2003 /// location within the file. `file_scope` union field will be active.
2004 entire_file,
2005 /// The source location points to a byte offset within a source file,
2006 /// offset from 0. The source file is determined contextually.
2007 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
2008 byte_abs: u32,
2009 /// The source location points to a token within a source file,
2010 /// offset from 0. The source file is determined contextually.
2011 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
2012 token_abs: u32,
2013 /// The source location points to an AST node within a source file,
2014 /// offset from 0. The source file is determined contextually.
2015 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
2016 node_abs: u32,
2017 /// The source location points to a byte offset within a source file,
2018 /// offset from the byte offset of the base node within the file.
2019 byte_offset: u32,
2020 /// This data is the offset into the token list from the base node's first token.
2021 token_offset: u32,
2022 /// The source location points to an AST node, which is this value offset
2023 /// from its containing base node AST index.
2024 node_offset: TracedOffset,
2025 /// The source location points to the main token of an AST node, found
2026 /// by taking this AST node index offset from the containing base node.
2027 node_offset_main_token: i32,
2028 /// The source location points to the beginning of a struct initializer.
2029 node_offset_initializer: i32,
2030 /// The source location points to a variable declaration type expression,
2031 /// found by taking this AST node index offset from the containing
2032 /// base node, which points to a variable declaration AST node. Next, navigate
2033 /// to the type expression.
2034 node_offset_var_decl_ty: i32,
2035 /// The source location points to the alignment expression of a var decl.
2036 node_offset_var_decl_align: i32,
2037 /// The source location points to the linksection expression of a var decl.
2038 node_offset_var_decl_section: i32,
2039 /// The source location points to the addrspace expression of a var decl.
2040 node_offset_var_decl_addrspace: i32,
2041 /// The source location points to the initializer of a var decl.
2042 node_offset_var_decl_init: i32,
2043 /// The source location points to the given argument of a builtin function call.
2044 /// `builtin_call_node` points to the builtin call.
2045 /// `arg_index` is the index of the argument which hte source location refers to.
2046 node_offset_builtin_call_arg: struct {
2047 builtin_call_node: i32,
2048 arg_index: u32,
2049 },
2050 /// Like `node_offset_builtin_call_arg` but recurses through arbitrarily many calls
2051 /// to pointer cast builtins (taking the first argument of the most nested).
2052 node_offset_ptrcast_operand: i32,
2053 /// The source location points to the index expression of an array access
2054 /// expression, found by taking this AST node index offset from the containing
2055 /// base node, which points to an array access AST node. Next, navigate
2056 /// to the index expression.
2057 node_offset_array_access_index: i32,
2058 /// The source location points to the LHS of a slice expression
2059 /// expression, found by taking this AST node index offset from the containing
2060 /// base node, which points to a slice AST node. Next, navigate
2061 /// to the sentinel expression.
2062 node_offset_slice_ptr: i32,
2063 /// The source location points to start expression of a slice expression
2064 /// expression, found by taking this AST node index offset from the containing
2065 /// base node, which points to a slice AST node. Next, navigate
2066 /// to the sentinel expression.
2067 node_offset_slice_start: i32,
2068 /// The source location points to the end expression of a slice
2069 /// expression, found by taking this AST node index offset from the containing
2070 /// base node, which points to a slice AST node. Next, navigate
2071 /// to the sentinel expression.
2072 node_offset_slice_end: i32,
2073 /// The source location points to the sentinel expression of a slice
2074 /// expression, found by taking this AST node index offset from the containing
2075 /// base node, which points to a slice AST node. Next, navigate
2076 /// to the sentinel expression.
2077 node_offset_slice_sentinel: i32,
2078 /// The source location points to the callee expression of a function
2079 /// call expression, found by taking this AST node index offset from the containing
2080 /// base node, which points to a function call AST node. Next, navigate
2081 /// to the callee expression.
2082 node_offset_call_func: i32,
2083 /// The payload is offset from the containing base node.
2084 /// The source location points to the field name of:
2085 /// * a field access expression (`a.b`), or
2086 /// * the callee of a method call (`a.b()`)
2087 node_offset_field_name: i32,
2088 /// The payload is offset from the containing base node.
2089 /// The source location points to the field name of the operand ("b" node)
2090 /// of a field initialization expression (`.a = b`)
2091 node_offset_field_name_init: i32,
2092 /// The source location points to the pointer of a pointer deref expression,
2093 /// found by taking this AST node index offset from the containing
2094 /// base node, which points to a pointer deref AST node. Next, navigate
2095 /// to the pointer expression.
2096 node_offset_deref_ptr: i32,
2097 /// The source location points to the assembly source code of an inline assembly
2098 /// expression, found by taking this AST node index offset from the containing
2099 /// base node, which points to inline assembly AST node. Next, navigate
2100 /// to the asm template source code.
2101 node_offset_asm_source: i32,
2102 /// The source location points to the return type of an inline assembly
2103 /// expression, found by taking this AST node index offset from the containing
2104 /// base node, which points to inline assembly AST node. Next, navigate
2105 /// to the return type expression.
2106 node_offset_asm_ret_ty: i32,
2107 /// The source location points to the condition expression of an if
2108 /// expression, found by taking this AST node index offset from the containing
2109 /// base node, which points to an if expression AST node. Next, navigate
2110 /// to the condition expression.
2111 node_offset_if_cond: i32,
2112 /// The source location points to a binary expression, such as `a + b`, found
2113 /// by taking this AST node index offset from the containing base node.
2114 node_offset_bin_op: i32,
2115 /// The source location points to the LHS of a binary expression, found
2116 /// by taking this AST node index offset from the containing base node,
2117 /// which points to a binary expression AST node. Next, navigate to the LHS.
2118 node_offset_bin_lhs: i32,
2119 /// The source location points to the RHS of a binary expression, found
2120 /// by taking this AST node index offset from the containing base node,
2121 /// which points to a binary expression AST node. Next, navigate to the RHS.
2122 node_offset_bin_rhs: i32,
2123 /// The source location points to the operand of a switch expression, found
2124 /// by taking this AST node index offset from the containing base node,
2125 /// which points to a switch expression AST node. Next, navigate to the operand.
2126 node_offset_switch_operand: i32,
2127 /// The source location points to the else/`_` prong of a switch expression, found
2128 /// by taking this AST node index offset from the containing base node,
2129 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.
2130 node_offset_switch_special_prong: i32,
2131 /// The source location points to all the ranges of a switch expression, found
2132 /// by taking this AST node index offset from the containing base node,
2133 /// which points to a switch expression AST node. Next, navigate to any of the
2134 /// range nodes. The error applies to all of them.
2135 node_offset_switch_range: i32,
2136 /// The source location points to the align expr of a function type
2137 /// expression, found by taking this AST node index offset from the containing
2138 /// base node, which points to a function type AST node. Next, navigate to
2139 /// the calling convention node.
2140 node_offset_fn_type_align: i32,
2141 /// The source location points to the addrspace expr of a function type
2142 /// expression, found by taking this AST node index offset from the containing
2143 /// base node, which points to a function type AST node. Next, navigate to
2144 /// the calling convention node.
2145 node_offset_fn_type_addrspace: i32,
2146 /// The source location points to the linksection expr of a function type
2147 /// expression, found by taking this AST node index offset from the containing
2148 /// base node, which points to a function type AST node. Next, navigate to
2149 /// the calling convention node.
2150 node_offset_fn_type_section: i32,
2151 /// The source location points to the calling convention of a function type
2152 /// expression, found by taking this AST node index offset from the containing
2153 /// base node, which points to a function type AST node. Next, navigate to
2154 /// the calling convention node.
2155 node_offset_fn_type_cc: i32,
2156 /// The source location points to the return type of a function type
2157 /// expression, found by taking this AST node index offset from the containing
2158 /// base node, which points to a function type AST node. Next, navigate to
2159 /// the return type node.
2160 node_offset_fn_type_ret_ty: i32,
2161 node_offset_param: i32,
2162 token_offset_param: i32,
2163 /// The source location points to the type expression of an `anyframe->T`
2164 /// expression, found by taking this AST node index offset from the containing
2165 /// base node, which points to a `anyframe->T` expression AST node. Next, navigate
2166 /// to the type expression.
2167 node_offset_anyframe_type: i32,
2168 /// The source location points to the string literal of `extern "foo"`, found
2169 /// by taking this AST node index offset from the containing
2170 /// base node, which points to a function prototype or variable declaration
2171 /// expression AST node. Next, navigate to the string literal of the `extern "foo"`.
2172 node_offset_lib_name: i32,
2173 /// The source location points to the len expression of an `[N:S]T`
2174 /// expression, found by taking this AST node index offset from the containing
2175 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2176 /// to the len expression.
2177 node_offset_array_type_len: i32,
2178 /// The source location points to the sentinel expression of an `[N:S]T`
2179 /// expression, found by taking this AST node index offset from the containing
2180 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2181 /// to the sentinel expression.
2182 node_offset_array_type_sentinel: i32,
2183 /// The source location points to the elem expression of an `[N:S]T`
2184 /// expression, found by taking this AST node index offset from the containing
2185 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate
2186 /// to the elem expression.
2187 node_offset_array_type_elem: i32,
2188 /// The source location points to the operand of an unary expression.
2189 node_offset_un_op: i32,
2190 /// The source location points to the elem type of a pointer.
2191 node_offset_ptr_elem: i32,
2192 /// The source location points to the sentinel of a pointer.
2193 node_offset_ptr_sentinel: i32,
2194 /// The source location points to the align expr of a pointer.
2195 node_offset_ptr_align: i32,
2196 /// The source location points to the addrspace expr of a pointer.
2197 node_offset_ptr_addrspace: i32,
2198 /// The source location points to the bit-offset of a pointer.
2199 node_offset_ptr_bitoffset: i32,
2200 /// The source location points to the host size of a pointer.
2201 node_offset_ptr_hostsize: i32,
2202 /// The source location points to the tag type of an union or an enum.
2203 node_offset_container_tag: i32,
2204 /// The source location points to the default value of a field.
2205 node_offset_field_default: i32,
2206 /// The source location points to the type of an array or struct initializer.
2207 node_offset_init_ty: i32,
2208 /// The source location points to the LHS of an assignment.
2209 node_offset_store_ptr: i32,
2210 /// The source location points to the RHS of an assignment.
2211 node_offset_store_operand: i32,
2212 /// The source location points to the operand of a `return` statement, or
2213 /// the `return` itself if there is no explicit operand.
2214 node_offset_return_operand: i32,
2215 /// The source location points to a for loop input.
2216 for_input: struct {
2217 /// Points to the for loop AST node.
2218 for_node_offset: i32,
2219 /// Picks one of the inputs from the condition.
2220 input_index: u32,
2221 },
2222 /// The source location points to one of the captures of a for loop, found
2223 /// by taking this AST node index offset from the containing
2224 /// base node, which points to one of the input nodes of a for loop.
2225 /// Next, navigate to the corresponding capture.
2226 for_capture_from_input: i32,
2227 /// The source location points to the argument node of a function call.
2228 call_arg: struct {
2229 /// Points to the function call AST node.
2230 call_node_offset: i32,
2231 /// The index of the argument the source location points to.
2232 arg_index: u32,
2233 },
2234 fn_proto_param: FnProtoParam,
2235 fn_proto_param_type: FnProtoParam,
2236 array_cat_lhs: ArrayCat,
2237 array_cat_rhs: ArrayCat,
2238 /// The source location points to the name of the field at the given index
2239 /// of the container type declaration at the base node.
2240 container_field_name: u32,
2241 /// Like `continer_field_name`, but points at the field's default value.
2242 container_field_value: u32,
2243 /// Like `continer_field_name`, but points at the field's type.
2244 container_field_type: u32,
2245 /// Like `continer_field_name`, but points at the field's alignment.
2246 container_field_align: u32,
2247 /// The source location points to the given element/field of a struct or
2248 /// array initialization expression.
2249 init_elem: struct {
2250 /// Points to the AST node of the initialization expression.
2251 init_node_offset: i32,
2252 /// The index of the field/element the source location points to.
2253 elem_index: u32,
2254 },
2255 // The following source locations are like `init_elem`, but refer to a
2256 // field with a specific name. If such a field is not given, the entire
2257 // initialization expression is used instead.
2258 // The `i32` points to the AST node of a builtin call, whose *second*
2259 // argument is the init expression.
2260 init_field_name: i32,
2261 init_field_linkage: i32,
2262 init_field_section: i32,
2263 init_field_visibility: i32,
2264 init_field_rw: i32,
2265 init_field_locality: i32,
2266 init_field_cache: i32,
2267 init_field_library: i32,
2268 init_field_thread_local: i32,
2269 /// The source location points to the value of an item in a specific
2270 /// case of a `switch`.
2271 switch_case_item: SwitchItem,
2272 /// The source location points to the "first" value of a range item in
2273 /// a specific case of a `switch`.
2274 switch_case_item_range_first: SwitchItem,
2275 /// The source location points to the "last" value of a range item in
2276 /// a specific case of a `switch`.
2277 switch_case_item_range_last: SwitchItem,
2278 /// The source location points to the main capture of a specific case of
2279 /// a `switch`.
2280 switch_capture: SwitchCapture,
2281 /// The source location points to the "tag" capture (second capture) of
2282 /// a specific case of a `switch`.
2283 switch_tag_capture: SwitchCapture,
2284
2285 pub const FnProtoParam = struct {
2286 /// The offset of the function prototype AST node.
2287 fn_proto_node_offset: i32,
2288 /// The index of the parameter the source location points to.
2289 param_index: u32,
2290 };
2291
2292 pub const SwitchItem = struct {
2293 /// The offset of the switch AST node.
2294 switch_node_offset: i32,
2295 /// The index of the case to point to within this switch.
2296 case_idx: SwitchCaseIndex,
2297 /// The index of the item to point to within this case.
2298 item_idx: SwitchItemIndex,
2299 };
2300
2301 pub const SwitchCapture = struct {
2302 /// The offset of the switch AST node.
2303 switch_node_offset: i32,
2304 /// The index of the case whose capture to point to.
2305 case_idx: SwitchCaseIndex,
2306 };
2307
2308 pub const SwitchCaseIndex = packed struct(u32) {
2309 kind: enum(u1) { scalar, multi },
2310 index: u31,
2311
2312 pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
2313 pub fn isSpecial(idx: SwitchCaseIndex) bool {
2314 return @as(u32, @bitCast(idx)) == @as(u32, @bitCast(special));
2315 }
2316 };
2317
2318 pub const SwitchItemIndex = packed struct(u32) {
2319 kind: enum(u1) { single, range },
2320 index: u31,
2321 };
2322
2323 const ArrayCat = struct {
2324 /// Points to the array concat AST node.
2325 array_cat_offset: i32,
2326 /// The index of the element the source location points to.
2327 elem_index: u32,
2328 };
2329
2330 pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
2331
2332 noinline fn nodeOffsetDebug(node_offset: i32) Offset {
2333 var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } };
2334 result.node_offset.trace.addAddr(@returnAddress(), "init");
2335 return result;
2336 }
2337
2338 fn nodeOffsetRelease(node_offset: i32) Offset {
2339 return .{ .node_offset = .{ .x = node_offset } };
2340 }
2341
2342 /// This wraps a simple integer in debug builds so that later on we can find out
2343 /// where in semantic analysis the value got set.
2344 pub const TracedOffset = struct {
2345 x: i32,
2346 trace: std.debug.Trace = std.debug.Trace.init,
2347
2348 const want_tracing = false;
2349 };
2350 };
2351
2352 pub const unneeded: LazySrcLoc = .{
2353 .base_node_inst = undefined,
2354 .offset = .unneeded,
2355 };
2356
2357 pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) struct { *File, Ast.Node.Index } {
2358 const want_path_digest, const zir_inst = inst: {
2359 const info = base_node_inst.resolveFull(&zcu.intern_pool);
2360 break :inst .{ info.path_digest, info.inst };
2361 };
2362 const file = file: {
2363 const index = zcu.path_digest_map.getIndex(want_path_digest).?;
2364 break :file zcu.import_table.values()[index];
2365 };
2366 assert(file.zir_loaded);
2367
2368 const zir = file.zir;
2369 const inst = zir.instructions.get(@intFromEnum(zir_inst));
2370 const base_node: Ast.Node.Index = switch (inst.tag) {
2371 .declaration => inst.data.declaration.src_node,
2372 .extended => switch (inst.data.extended.opcode) {
2373 .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_node,
2374 .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_node,
2375 .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_node,
2376 .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_node,
2377 .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.node,
2378 else => unreachable,
2379 },
2380 else => unreachable,
2381 };
2382 return .{ file, base_node };
2383 }
2384
2385 /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`.
2386 /// TODO: it is incorrect to store a `SrcLoc` anywhere due to incremental compilation.
2387 /// Probably the type should be removed entirely and this resolution performed on-the-fly when needed.
2388 pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc {
2389 const file, const base_node = resolveBaseNode(lazy.base_node_inst, zcu);
2390 return .{
2391 .file_scope = file,
2392 .base_node = base_node,
2393 .lazy = lazy.offset,
2394 };
2395 }
2396};
2397
2398pub const SemaError = error{ OutOfMemory, AnalysisFail };
2399pub const CompileError = error{
2400 OutOfMemory,
2401 /// When this is returned, the compile error for the failure has already been recorded.
2402 AnalysisFail,
2403 /// A Type or Value was needed to be used during semantic analysis, but it was not available
2404 /// because the function is generic. This is only seen when analyzing the body of a param
2405 /// instruction.
2406 GenericPoison,
2407 /// In a comptime scope, a return instruction was encountered. This error is only seen when
2408 /// doing a comptime function call.
2409 ComptimeReturn,
2410 /// In a comptime scope, a break instruction was encountered. This error is only seen when
2411 /// evaluating a comptime block.
2412 ComptimeBreak,
2413};
2414
2415pub fn init(mod: *Module) !void {
2416 const gpa = mod.gpa;
2417 try mod.intern_pool.init(gpa);
2418 try mod.global_error_set.put(gpa, .empty, {});
2419}
2420
2421pub fn deinit(zcu: *Zcu) void {
2422 const gpa = zcu.gpa;
2423
2424 if (zcu.llvm_object) |llvm_object| {
2425 if (build_options.only_c) unreachable;
2426 llvm_object.deinit();
2427 }
2428
2429 for (zcu.import_table.keys()) |key| {
2430 gpa.free(key);
2431 }
2432 var failed_decls = zcu.failed_decls;
2433 zcu.failed_decls = .{};
2434 for (zcu.import_table.values()) |value| {
2435 value.destroy(zcu);
2436 }
2437 zcu.import_table.deinit(gpa);
2438 zcu.path_digest_map.deinit(gpa);
2439
2440 for (zcu.embed_table.keys(), zcu.embed_table.values()) |path, embed_file| {
2441 gpa.free(path);
2442 gpa.destroy(embed_file);
2443 }
2444 zcu.embed_table.deinit(gpa);
2445
2446 zcu.compile_log_text.deinit(gpa);
2447
2448 zcu.local_zir_cache.handle.close();
2449 zcu.global_zir_cache.handle.close();
2450
2451 for (failed_decls.values()) |value| {
2452 value.destroy(gpa);
2453 }
2454 failed_decls.deinit(gpa);
2455
2456 if (zcu.emit_h) |emit_h| {
2457 for (emit_h.failed_decls.values()) |value| {
2458 value.destroy(gpa);
2459 }
2460 emit_h.failed_decls.deinit(gpa);
2461 emit_h.decl_table.deinit(gpa);
2462 emit_h.allocated_emit_h.deinit(gpa);
2463 }
2464
2465 for (zcu.failed_files.values()) |value| {
2466 if (value) |msg| msg.destroy(gpa);
2467 }
2468 zcu.failed_files.deinit(gpa);
2469
2470 for (zcu.failed_embed_files.values()) |msg| {
2471 msg.destroy(gpa);
2472 }
2473 zcu.failed_embed_files.deinit(gpa);
2474
2475 for (zcu.failed_exports.values()) |value| {
2476 value.destroy(gpa);
2477 }
2478 zcu.failed_exports.deinit(gpa);
2479
2480 for (zcu.cimport_errors.values()) |*errs| {
2481 errs.deinit(gpa);
2482 }
2483 zcu.cimport_errors.deinit(gpa);
2484
2485 zcu.compile_log_decls.deinit(gpa);
2486
2487 for (zcu.decl_exports.values()) |*export_list| {
2488 export_list.deinit(gpa);
2489 }
2490 zcu.decl_exports.deinit(gpa);
2491
2492 for (zcu.value_exports.values()) |*export_list| {
2493 export_list.deinit(gpa);
2494 }
2495 zcu.value_exports.deinit(gpa);
2496
2497 for (zcu.export_owners.values()) |*value| {
2498 freeExportList(gpa, value);
2499 }
2500 zcu.export_owners.deinit(gpa);
2501
2502 zcu.global_error_set.deinit(gpa);
2503
2504 zcu.potentially_outdated.deinit(gpa);
2505 zcu.outdated.deinit(gpa);
2506 zcu.outdated_ready.deinit(gpa);
2507 zcu.outdated_file_root.deinit(gpa);
2508 zcu.retryable_failures.deinit(gpa);
2509
2510 zcu.test_functions.deinit(gpa);
2511
2512 for (zcu.global_assembly.values()) |s| {
2513 gpa.free(s);
2514 }
2515 zcu.global_assembly.deinit(gpa);
2516
2517 zcu.reference_table.deinit(gpa);
2518
2519 {
2520 var it = zcu.intern_pool.allocated_namespaces.iterator(0);
2521 while (it.next()) |namespace| {
2522 namespace.decls.deinit(gpa);
2523 namespace.usingnamespace_set.deinit(gpa);
2524 }
2525 }
2526
2527 zcu.intern_pool.deinit(gpa);
2528}
2529
2530pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
2531 const gpa = mod.gpa;
2532 const ip = &mod.intern_pool;
2533
2534 {
2535 _ = mod.test_functions.swapRemove(decl_index);
2536 if (mod.global_assembly.fetchSwapRemove(decl_index)) |kv| {
2537 gpa.free(kv.value);
2538 }
2539 }
2540
2541 ip.destroyDecl(gpa, decl_index);
2542
2543 if (mod.emit_h) |mod_emit_h| {
2544 const decl_emit_h = mod_emit_h.declPtr(decl_index);
2545 decl_emit_h.fwd_decl.deinit(gpa);
2546 decl_emit_h.* = undefined;
2547 }
2548}
2549
2550pub fn declPtr(mod: *Module, index: Decl.Index) *Decl {
2551 return mod.intern_pool.declPtr(index);
2552}
2553
2554pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace {
2555 return mod.intern_pool.namespacePtr(index);
2556}
2557
2558pub fn namespacePtrUnwrap(mod: *Module, index: Namespace.OptionalIndex) ?*Namespace {
2559 return mod.namespacePtr(index.unwrap() orelse return null);
2560}
2561
2562/// Returns true if and only if the Decl is the top level struct associated with a File.
2563pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
2564 const decl = mod.declPtr(decl_index);
2565 const namespace = mod.namespacePtr(decl.src_namespace);
2566 if (namespace.parent != .none) return false;
2567 return decl_index == namespace.decl_index;
2568}
2569
2570fn freeExportList(gpa: Allocator, export_list: *ArrayListUnmanaged(*Export)) void {
2571 for (export_list.items) |exp| gpa.destroy(exp);
2572 export_list.deinit(gpa);
2573}
2574
2575// TODO https://github.com/ziglang/zig/issues/8643
2576const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8;
2577const HackDataLayout = extern struct {
2578 data: [8]u8 align(@alignOf(Zir.Inst.Data)),
2579 safety_tag: u8,
2580};
2581comptime {
2582 if (data_has_safety_tag) {
2583 assert(@sizeOf(HackDataLayout) == @sizeOf(Zir.Inst.Data));
2584 }
2585}
2586
2587pub fn astGenFile(mod: *Module, file: *File) !void {
2588 assert(!file.mod.isBuiltin());
2589
2590 const tracy = trace(@src());
2591 defer tracy.end();
2592
2593 const comp = mod.comp;
2594 const gpa = mod.gpa;
2595
2596 // In any case we need to examine the stat of the file to determine the course of action.
2597 var source_file = try file.mod.root.openFile(file.sub_file_path, .{});
2598 defer source_file.close();
2599
2600 const stat = try source_file.stat();
2601
2602 const want_local_cache = file.mod == mod.main_mod;
2603 const hex_digest = hex: {
2604 var hex: Cache.HexDigest = undefined;
2605 _ = std.fmt.bufPrint(
2606 &hex,
2607 "{s}",
2608 .{std.fmt.fmtSliceHexLower(&file.path_digest)},
2609 ) catch unreachable;
2610 break :hex hex;
2611 };
2612 const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache;
2613 const zir_dir = cache_directory.handle;
2614
2615 // Determine whether we need to reload the file from disk and redo parsing and AstGen.
2616 var lock: std.fs.File.Lock = switch (file.status) {
2617 .never_loaded, .retryable_failure => lock: {
2618 // First, load the cached ZIR code, if any.
2619 log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{
2620 file.sub_file_path, want_local_cache, &hex_digest,
2621 });
2622
2623 break :lock .shared;
2624 },
2625 .parse_failure, .astgen_failure, .success_zir => lock: {
2626 const unchanged_metadata =
2627 stat.size == file.stat.size and
2628 stat.mtime == file.stat.mtime and
2629 stat.inode == file.stat.inode;
2630
2631 if (unchanged_metadata) {
2632 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
2633 return;
2634 }
2635
2636 log.debug("metadata changed: {s}", .{file.sub_file_path});
2637
2638 break :lock .exclusive;
2639 },
2640 };
2641
2642 // We ask for a lock in order to coordinate with other zig processes.
2643 // If another process is already working on this file, we will get the cached
2644 // version. Likewise if we're working on AstGen and another process asks for
2645 // the cached file, they'll get it.
2646 const cache_file = while (true) {
2647 break zir_dir.createFile(&hex_digest, .{
2648 .read = true,
2649 .truncate = false,
2650 .lock = lock,
2651 }) catch |err| switch (err) {
2652 error.NotDir => unreachable, // no dir components
2653 error.InvalidUtf8 => unreachable, // it's a hex encoded name
2654 error.InvalidWtf8 => unreachable, // it's a hex encoded name
2655 error.BadPathName => unreachable, // it's a hex encoded name
2656 error.NameTooLong => unreachable, // it's a fixed size name
2657 error.PipeBusy => unreachable, // it's not a pipe
2658 error.WouldBlock => unreachable, // not asking for non-blocking I/O
2659 // There are no dir components, so you would think that this was
2660 // unreachable, however we have observed on macOS two processes racing
2661 // to do openat() with O_CREAT manifest in ENOENT.
2662 error.FileNotFound => continue,
2663
2664 else => |e| return e, // Retryable errors are handled at callsite.
2665 };
2666 };
2667 defer cache_file.close();
2668
2669 while (true) {
2670 update: {
2671 // First we read the header to determine the lengths of arrays.
2672 const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) {
2673 // This can happen if Zig bails out of this function between creating
2674 // the cached file and writing it.
2675 error.EndOfStream => break :update,
2676 else => |e| return e,
2677 };
2678 const unchanged_metadata =
2679 stat.size == header.stat_size and
2680 stat.mtime == header.stat_mtime and
2681 stat.inode == header.stat_inode;
2682
2683 if (!unchanged_metadata) {
2684 log.debug("AstGen cache stale: {s}", .{file.sub_file_path});
2685 break :update;
2686 }
2687 log.debug("AstGen cache hit: {s} instructions_len={d}", .{
2688 file.sub_file_path, header.instructions_len,
2689 });
2690
2691 file.zir = loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) {
2692 error.UnexpectedFileSize => {
2693 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path});
2694 break :update;
2695 },
2696 else => |e| return e,
2697 };
2698 file.zir_loaded = true;
2699 file.stat = .{
2700 .size = header.stat_size,
2701 .inode = header.stat_inode,
2702 .mtime = header.stat_mtime,
2703 };
2704 file.status = .success_zir;
2705 log.debug("AstGen cached success: {s}", .{file.sub_file_path});
2706
2707 // TODO don't report compile errors until Sema @importFile
2708 if (file.zir.hasCompileErrors()) {
2709 {
2710 comp.mutex.lock();
2711 defer comp.mutex.unlock();
2712 try mod.failed_files.putNoClobber(gpa, file, null);
2713 }
2714 file.status = .astgen_failure;
2715 return error.AnalysisFail;
2716 }
2717 return;
2718 }
2719
2720 // If we already have the exclusive lock then it is our job to update.
2721 if (builtin.os.tag == .wasi or lock == .exclusive) break;
2722 // Otherwise, unlock to give someone a chance to get the exclusive lock
2723 // and then upgrade to an exclusive lock.
2724 cache_file.unlock();
2725 lock = .exclusive;
2726 try cache_file.lock(lock);
2727 }
2728
2729 // The cache is definitely stale so delete the contents to avoid an underwrite later.
2730 cache_file.setEndPos(0) catch |err| switch (err) {
2731 error.FileTooBig => unreachable, // 0 is not too big
2732
2733 else => |e| return e,
2734 };
2735
2736 mod.lockAndClearFileCompileError(file);
2737
2738 // If the previous ZIR does not have compile errors, keep it around
2739 // in case parsing or new ZIR fails. In case of successful ZIR update
2740 // at the end of this function we will free it.
2741 // We keep the previous ZIR loaded so that we can use it
2742 // for the update next time it does not have any compile errors. This avoids
2743 // needlessly tossing out semantic analysis work when an error is
2744 // temporarily introduced.
2745 if (file.zir_loaded and !file.zir.hasCompileErrors()) {
2746 assert(file.prev_zir == null);
2747 const prev_zir_ptr = try gpa.create(Zir);
2748 file.prev_zir = prev_zir_ptr;
2749 prev_zir_ptr.* = file.zir;
2750 file.zir = undefined;
2751 file.zir_loaded = false;
2752 }
2753 file.unload(gpa);
2754
2755 if (stat.size > std.math.maxInt(u32))
2756 return error.FileTooBig;
2757
2758 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
2759 defer if (!file.source_loaded) gpa.free(source);
2760 const amt = try source_file.readAll(source);
2761 if (amt != stat.size)
2762 return error.UnexpectedEndOfFile;
2763
2764 file.stat = .{
2765 .size = stat.size,
2766 .inode = stat.inode,
2767 .mtime = stat.mtime,
2768 };
2769 file.source = source;
2770 file.source_loaded = true;
2771
2772 file.tree = try Ast.parse(gpa, source, .zig);
2773 file.tree_loaded = true;
2774
2775 // Any potential AST errors are converted to ZIR errors here.
2776 file.zir = try AstGen.generate(gpa, file.tree);
2777 file.zir_loaded = true;
2778 file.status = .success_zir;
2779 log.debug("AstGen fresh success: {s}", .{file.sub_file_path});
2780
2781 const safety_buffer = if (data_has_safety_tag)
2782 try gpa.alloc([8]u8, file.zir.instructions.len)
2783 else
2784 undefined;
2785 defer if (data_has_safety_tag) gpa.free(safety_buffer);
2786 const data_ptr = if (data_has_safety_tag)
2787 if (file.zir.instructions.len == 0)
2788 @as([*]const u8, undefined)
2789 else
2790 @as([*]const u8, @ptrCast(safety_buffer.ptr))
2791 else
2792 @as([*]const u8, @ptrCast(file.zir.instructions.items(.data).ptr));
2793 if (data_has_safety_tag) {
2794 // The `Data` union has a safety tag but in the file format we store it without.
2795 for (file.zir.instructions.items(.data), 0..) |*data, i| {
2796 const as_struct = @as(*const HackDataLayout, @ptrCast(data));
2797 safety_buffer[i] = as_struct.data;
2798 }
2799 }
2800
2801 const header: Zir.Header = .{
2802 .instructions_len = @as(u32, @intCast(file.zir.instructions.len)),
2803 .string_bytes_len = @as(u32, @intCast(file.zir.string_bytes.len)),
2804 .extra_len = @as(u32, @intCast(file.zir.extra.len)),
2805
2806 .stat_size = stat.size,
2807 .stat_inode = stat.inode,
2808 .stat_mtime = stat.mtime,
2809 };
2810 var iovecs = [_]std.posix.iovec_const{
2811 .{
2812 .base = @as([*]const u8, @ptrCast(&header)),
2813 .len = @sizeOf(Zir.Header),
2814 },
2815 .{
2816 .base = @as([*]const u8, @ptrCast(file.zir.instructions.items(.tag).ptr)),
2817 .len = file.zir.instructions.len,
2818 },
2819 .{
2820 .base = data_ptr,
2821 .len = file.zir.instructions.len * 8,
2822 },
2823 .{
2824 .base = file.zir.string_bytes.ptr,
2825 .len = file.zir.string_bytes.len,
2826 },
2827 .{
2828 .base = @as([*]const u8, @ptrCast(file.zir.extra.ptr)),
2829 .len = file.zir.extra.len * 4,
2830 },
2831 };
2832 cache_file.writevAll(&iovecs) catch |err| {
2833 log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{
2834 file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err),
2835 });
2836 };
2837
2838 if (file.zir.hasCompileErrors()) {
2839 {
2840 comp.mutex.lock();
2841 defer comp.mutex.unlock();
2842 try mod.failed_files.putNoClobber(gpa, file, null);
2843 }
2844 file.status = .astgen_failure;
2845 return error.AnalysisFail;
2846 }
2847
2848 if (file.prev_zir) |prev_zir| {
2849 try updateZirRefs(mod, file, prev_zir.*);
2850 // No need to keep previous ZIR.
2851 prev_zir.deinit(gpa);
2852 gpa.destroy(prev_zir);
2853 file.prev_zir = null;
2854 }
2855
2856 if (file.root_decl.unwrap()) |root_decl| {
2857 // The root of this file must be re-analyzed, since the file has changed.
2858 comp.mutex.lock();
2859 defer comp.mutex.unlock();
2860
2861 log.debug("outdated root Decl: {}", .{root_decl});
2862 try mod.outdated_file_root.put(gpa, root_decl, {});
2863 }
2864}
2865
2866pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir {
2867 return loadZirCacheBody(gpa, try cache_file.reader().readStruct(Zir.Header), cache_file);
2868}
2869
2870fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File) !Zir {
2871 var instructions: std.MultiArrayList(Zir.Inst) = .{};
2872 errdefer instructions.deinit(gpa);
2873
2874 try instructions.setCapacity(gpa, header.instructions_len);
2875 instructions.len = header.instructions_len;
2876
2877 var zir: Zir = .{
2878 .instructions = instructions.toOwnedSlice(),
2879 .string_bytes = &.{},
2880 .extra = &.{},
2881 };
2882 errdefer zir.deinit(gpa);
2883
2884 zir.string_bytes = try gpa.alloc(u8, header.string_bytes_len);
2885 zir.extra = try gpa.alloc(u32, header.extra_len);
2886
2887 const safety_buffer = if (data_has_safety_tag)
2888 try gpa.alloc([8]u8, header.instructions_len)
2889 else
2890 undefined;
2891 defer if (data_has_safety_tag) gpa.free(safety_buffer);
2892
2893 const data_ptr = if (data_has_safety_tag)
2894 @as([*]u8, @ptrCast(safety_buffer.ptr))
2895 else
2896 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr));
2897
2898 var iovecs = [_]std.posix.iovec{
2899 .{
2900 .base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)),
2901 .len = header.instructions_len,
2902 },
2903 .{
2904 .base = data_ptr,
2905 .len = header.instructions_len * 8,
2906 },
2907 .{
2908 .base = zir.string_bytes.ptr,
2909 .len = header.string_bytes_len,
2910 },
2911 .{
2912 .base = @as([*]u8, @ptrCast(zir.extra.ptr)),
2913 .len = header.extra_len * 4,
2914 },
2915 };
2916 const amt_read = try cache_file.readvAll(&iovecs);
2917 const amt_expected = zir.instructions.len * 9 +
2918 zir.string_bytes.len +
2919 zir.extra.len * 4;
2920 if (amt_read != amt_expected) return error.UnexpectedFileSize;
2921 if (data_has_safety_tag) {
2922 const tags = zir.instructions.items(.tag);
2923 for (zir.instructions.items(.data), 0..) |*data, i| {
2924 const union_tag = Zir.Inst.Tag.data_tags[@intFromEnum(tags[i])];
2925 const as_struct = @as(*HackDataLayout, @ptrCast(data));
2926 as_struct.* = .{
2927 .safety_tag = @intFromEnum(union_tag),
2928 .data = safety_buffer[i],
2929 };
2930 }
2931 }
2932
2933 return zir;
2934}
2935
2936/// This is called from the AstGen thread pool, so must acquire
2937/// the Compilation mutex when acting on shared state.
2938fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void {
2939 const gpa = zcu.gpa;
2940 const new_zir = file.zir;
2941
2942 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
2943 defer inst_map.deinit(gpa);
2944
2945 try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map);
2946
2947 const old_tag = old_zir.instructions.items(.tag);
2948 const old_data = old_zir.instructions.items(.data);
2949
2950 // TODO: this should be done after all AstGen workers complete, to avoid
2951 // iterating over this full set for every updated file.
2952 for (zcu.intern_pool.tracked_insts.keys(), 0..) |*ti, idx_raw| {
2953 const ti_idx: InternPool.TrackedInst.Index = @enumFromInt(idx_raw);
2954 if (!std.mem.eql(u8, &ti.path_digest, &file.path_digest)) continue;
2955 const old_inst = ti.inst;
2956 ti.inst = inst_map.get(ti.inst) orelse {
2957 // Tracking failed for this instruction. Invalidate associated `src_hash` deps.
2958 zcu.comp.mutex.lock();
2959 defer zcu.comp.mutex.unlock();
2960 log.debug("tracking failed for %{d}", .{old_inst});
2961 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx });
2962 continue;
2963 };
2964
2965 if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
2966 if (new_zir.getAssociatedSrcHash(ti.inst)) |new_hash| {
2967 if (std.zig.srcHashEql(old_hash, new_hash)) {
2968 break :hash_changed;
2969 }
2970 log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{
2971 old_inst,
2972 ti.inst,
2973 std.fmt.fmtSliceHexLower(&old_hash),
2974 std.fmt.fmtSliceHexLower(&new_hash),
2975 });
2976 }
2977 // The source hash associated with this instruction changed - invalidate relevant dependencies.
2978 zcu.comp.mutex.lock();
2979 defer zcu.comp.mutex.unlock();
2980 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx });
2981 }
2982
2983 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
2984 const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
2985 .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
2986 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
2987 else => false,
2988 },
2989 else => false,
2990 };
2991 if (!has_namespace) continue;
2992
2993 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
2994 defer old_names.deinit(zcu.gpa);
2995 {
2996 var it = old_zir.declIterator(old_inst);
2997 while (it.next()) |decl_inst| {
2998 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
2999 switch (decl_name) {
3000 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
3001 _ => if (decl_name.isNamedTest(old_zir)) continue,
3002 }
3003 const name_zir = decl_name.toString(old_zir).?;
3004 const name_ip = try zcu.intern_pool.getOrPutString(
3005 zcu.gpa,
3006 old_zir.nullTerminatedString(name_zir),
3007 .no_embedded_nulls,
3008 );
3009 try old_names.put(zcu.gpa, name_ip, {});
3010 }
3011 }
3012 var any_change = false;
3013 {
3014 var it = new_zir.declIterator(ti.inst);
3015 while (it.next()) |decl_inst| {
3016 const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
3017 switch (decl_name) {
3018 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
3019 _ => if (decl_name.isNamedTest(old_zir)) continue,
3020 }
3021 const name_zir = decl_name.toString(old_zir).?;
3022 const name_ip = try zcu.intern_pool.getOrPutString(
3023 zcu.gpa,
3024 old_zir.nullTerminatedString(name_zir),
3025 .no_embedded_nulls,
3026 );
3027 if (!old_names.swapRemove(name_ip)) continue;
3028 // Name added
3029 any_change = true;
3030 zcu.comp.mutex.lock();
3031 defer zcu.comp.mutex.unlock();
3032 try zcu.markDependeeOutdated(.{ .namespace_name = .{
3033 .namespace = ti_idx,
3034 .name = name_ip,
3035 } });
3036 }
3037 }
3038 // The only elements remaining in `old_names` now are any names which were removed.
3039 for (old_names.keys()) |name_ip| {
3040 any_change = true;
3041 zcu.comp.mutex.lock();
3042 defer zcu.comp.mutex.unlock();
3043 try zcu.markDependeeOutdated(.{ .namespace_name = .{
3044 .namespace = ti_idx,
3045 .name = name_ip,
3046 } });
3047 }
3048
3049 if (any_change) {
3050 zcu.comp.mutex.lock();
3051 defer zcu.comp.mutex.unlock();
3052 try zcu.markDependeeOutdated(.{ .namespace = ti_idx });
3053 }
3054 }
3055}
3056
3057pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3058 log.debug("outdated dependee: {}", .{dependee});
3059 var it = zcu.intern_pool.dependencyIterator(dependee);
3060 while (it.next()) |depender| {
3061 if (zcu.outdated.contains(depender)) {
3062 // We do not need to increment the PO dep count, as if the outdated
3063 // dependee is a Decl, we had already marked this as PO.
3064 continue;
3065 }
3066 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender);
3067 try zcu.outdated.putNoClobber(
3068 zcu.gpa,
3069 depender,
3070 // We do not need to increment this count for the same reason as above.
3071 if (opt_po_entry) |e| e.value else 0,
3072 );
3073 log.debug("outdated: {}", .{depender});
3074 if (opt_po_entry == null) {
3075 // This is a new entry with no PO dependencies.
3076 try zcu.outdated_ready.put(zcu.gpa, depender, {});
3077 }
3078 // If this is a Decl and was not previously PO, we must recursively
3079 // mark dependencies on its tyval as PO.
3080 if (opt_po_entry == null) {
3081 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
3082 }
3083 }
3084}
3085
3086fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
3087 var it = zcu.intern_pool.dependencyIterator(dependee);
3088 while (it.next()) |depender| {
3089 if (zcu.outdated.getPtr(depender)) |po_dep_count| {
3090 // This depender is already outdated, but it now has one
3091 // less PO dependency!
3092 po_dep_count.* -= 1;
3093 if (po_dep_count.* == 0) {
3094 try zcu.outdated_ready.put(zcu.gpa, depender, {});
3095 }
3096 continue;
3097 }
3098 // This depender is definitely at least PO, because this Decl was just analyzed
3099 // due to being outdated.
3100 const ptr = zcu.potentially_outdated.getPtr(depender).?;
3101 if (ptr.* > 1) {
3102 ptr.* -= 1;
3103 continue;
3104 }
3105
3106 // This dependency is no longer PO, i.e. is known to be up-to-date.
3107 assert(zcu.potentially_outdated.swapRemove(depender));
3108 // If this is a Decl, we must recursively mark dependencies on its tyval
3109 // as no longer PO.
3110 switch (depender.unwrap()) {
3111 .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }),
3112 .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }),
3113 }
3114 }
3115}
3116
3117/// Given a Depender which is newly outdated or PO, mark all Dependers which may
3118/// in turn be PO, due to a dependency on the original Depender's tyval or IES.
3119fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: InternPool.Depender) !void {
3120 var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) {
3121 .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced
3122 .func => |func_index| .{ .func_ies = func_index },
3123 });
3124
3125 while (it.next()) |po| {
3126 if (zcu.outdated.getPtr(po)) |po_dep_count| {
3127 // This dependency is already outdated, but it now has one more PO
3128 // dependency.
3129 if (po_dep_count.* == 0) {
3130 _ = zcu.outdated_ready.swapRemove(po);
3131 }
3132 po_dep_count.* += 1;
3133 continue;
3134 }
3135 if (zcu.potentially_outdated.getPtr(po)) |n| {
3136 // There is now one more PO dependency.
3137 n.* += 1;
3138 continue;
3139 }
3140 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
3141 // This Depender was not already PO, so we must recursively mark its dependers as also PO.
3142 try zcu.markTransitiveDependersPotentiallyOutdated(po);
3143 }
3144}
3145
3146pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender {
3147 if (!zcu.comp.debug_incremental) return null;
3148
3149 if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) {
3150 log.debug("findOutdatedToAnalyze: no outdated depender", .{});
3151 return null;
3152 }
3153
3154 // Our goal is to find an outdated Depender which itself has no outdated or
3155 // PO dependencies. Most of the time, such a Depender will exist - we track
3156 // them in the `outdated_ready` set for efficiency. However, this is not
3157 // necessarily the case, since the Decl dependency graph may contain loops
3158 // via mutually recursive definitions:
3159 // pub const A = struct { b: *B };
3160 // pub const B = struct { b: *A };
3161 // In this case, we must defer to more complex logic below.
3162
3163 if (zcu.outdated_ready.count() > 0) {
3164 log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{
3165 @tagName(zcu.outdated_ready.keys()[0].unwrap()),
3166 switch (zcu.outdated_ready.keys()[0].unwrap()) {
3167 inline else => |x| @intFromEnum(x),
3168 },
3169 });
3170 return zcu.outdated_ready.keys()[0];
3171 }
3172
3173 // Next, we will see if there is any outdated file root which was not in
3174 // `outdated`. This set will be small (number of files changed in this
3175 // update), so it's alright for us to just iterate here.
3176 for (zcu.outdated_file_root.keys()) |file_decl| {
3177 const decl_depender = InternPool.Depender.wrap(.{ .decl = file_decl });
3178 if (zcu.outdated.contains(decl_depender)) {
3179 // Since we didn't hit this in the first loop, this Decl must have
3180 // pending dependencies, so is ineligible.
3181 continue;
3182 }
3183 if (zcu.potentially_outdated.contains(decl_depender)) {
3184 // This Decl's struct may or may not need to be recreated depending
3185 // on whether it is outdated. If we analyzed it now, we would have
3186 // to assume it was outdated and recreate it!
3187 continue;
3188 }
3189 log.debug("findOutdatedToAnalyze: outdated file root decl '{d}'", .{file_decl});
3190 return decl_depender;
3191 }
3192
3193 // There is no single Depender which is ready for re-analysis. Instead, we
3194 // must assume that some Decl with PO dependencies is outdated - e.g. in the
3195 // above example we arbitrarily pick one of A or B. We should select a Decl,
3196 // since a Decl is definitely responsible for the loop in the dependency
3197 // graph (since you can't depend on a runtime function analysis!).
3198
3199 // The choice of this Decl could have a big impact on how much total
3200 // analysis we perform, since if analysis concludes its tyval is unchanged,
3201 // then other PO Dependers may be resolved as up-to-date. To hopefully avoid
3202 // doing too much work, let's find a Decl which the most things depend on -
3203 // the idea is that this will resolve a lot of loops (but this is only a
3204 // heuristic).
3205
3206 log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{
3207 zcu.outdated.count(),
3208 zcu.potentially_outdated.count(),
3209 });
3210
3211 var chosen_decl_idx: ?Decl.Index = null;
3212 var chosen_decl_dependers: u32 = undefined;
3213
3214 for (zcu.outdated.keys()) |depender| {
3215 const decl_index = switch (depender.unwrap()) {
3216 .decl => |d| d,
3217 .func => continue,
3218 };
3219
3220 var n: u32 = 0;
3221 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });
3222 while (it.next()) |_| n += 1;
3223
3224 if (chosen_decl_idx == null or n > chosen_decl_dependers) {
3225 chosen_decl_idx = decl_index;
3226 chosen_decl_dependers = n;
3227 }
3228 }
3229
3230 for (zcu.potentially_outdated.keys()) |depender| {
3231 const decl_index = switch (depender.unwrap()) {
3232 .decl => |d| d,
3233 .func => continue,
3234 };
3235
3236 var n: u32 = 0;
3237 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index });
3238 while (it.next()) |_| n += 1;
3239
3240 if (chosen_decl_idx == null or n > chosen_decl_dependers) {
3241 chosen_decl_idx = decl_index;
3242 chosen_decl_dependers = n;
3243 }
3244 }
3245
3246 log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{
3247 chosen_decl_idx.?,
3248 chosen_decl_dependers,
3249 });
3250
3251 return InternPool.Depender.wrap(.{ .decl = chosen_decl_idx.? });
3252}
3253
3254/// During an incremental update, before semantic analysis, call this to flush all values from
3255/// `retryable_failures` and mark them as outdated so they get re-analyzed.
3256pub fn flushRetryableFailures(zcu: *Zcu) !void {
3257 const gpa = zcu.gpa;
3258 for (zcu.retryable_failures.items) |depender| {
3259 if (zcu.outdated.contains(depender)) continue;
3260 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| {
3261 // This Depender was already PO, but we now consider it outdated.
3262 // Any transitive dependencies are already marked PO.
3263 try zcu.outdated.put(gpa, depender, kv.value);
3264 continue;
3265 }
3266 // This Depender was not marked PO, but is now outdated. Mark it as
3267 // such, then recursively mark transitive dependencies as PO.
3268 try zcu.outdated.put(gpa, depender, 0);
3269 try zcu.markTransitiveDependersPotentiallyOutdated(depender);
3270 }
3271 zcu.retryable_failures.clearRetainingCapacity();
3272}
3273
3274pub fn mapOldZirToNew(
3275 gpa: Allocator,
3276 old_zir: Zir,
3277 new_zir: Zir,
3278 inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
3279) Allocator.Error!void {
3280 // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc.
3281 // Not `declaration`, as this does not create a namespace.
3282 const MatchedZirDecl = struct {
3283 old_inst: Zir.Inst.Index,
3284 new_inst: Zir.Inst.Index,
3285 };
3286 var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{};
3287 defer match_stack.deinit(gpa);
3288
3289 // Main struct inst is always matched
3290 try match_stack.append(gpa, .{
3291 .old_inst = .main_struct_inst,
3292 .new_inst = .main_struct_inst,
3293 });
3294
3295 // Used as temporary buffers for namespace declaration instructions
3296 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
3297 defer old_decls.deinit();
3298 var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
3299 defer new_decls.deinit();
3300
3301 while (match_stack.popOrNull()) |match_item| {
3302 // Match the namespace declaration itself
3303 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);
3304
3305 // Maps decl name to `declaration` instruction.
3306 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{};
3307 defer named_decls.deinit(gpa);
3308 // Maps test name to `declaration` instruction.
3309 var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{};
3310 defer named_tests.deinit(gpa);
3311 // All unnamed tests, in order, for a best-effort match.
3312 var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
3313 defer unnamed_tests.deinit(gpa);
3314 // All comptime declarations, in order, for a best-effort match.
3315 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
3316 defer comptime_decls.deinit(gpa);
3317 // All usingnamespace declarations, in order, for a best-effort match.
3318 var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
3319 defer usingnamespace_decls.deinit(gpa);
3320
3321 {
3322 var old_decl_it = old_zir.declIterator(match_item.old_inst);
3323 while (old_decl_it.next()) |old_decl_inst| {
3324 const old_decl, _ = old_zir.getDeclaration(old_decl_inst);
3325 switch (old_decl.name) {
3326 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
3327 .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
3328 .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst),
3329 _ => {
3330 const name_nts = old_decl.name.toString(old_zir).?;
3331 const name = old_zir.nullTerminatedString(name_nts);
3332 if (old_decl.name.isNamedTest(old_zir)) {
3333 try named_tests.put(gpa, name, old_decl_inst);
3334 } else {
3335 try named_decls.put(gpa, name, old_decl_inst);
3336 }
3337 },
3338 }
3339 }
3340 }
3341
3342 var unnamed_test_idx: u32 = 0;
3343 var comptime_decl_idx: u32 = 0;
3344 var usingnamespace_decl_idx: u32 = 0;
3345
3346 var new_decl_it = new_zir.declIterator(match_item.new_inst);
3347 while (new_decl_it.next()) |new_decl_inst| {
3348 const new_decl, _ = new_zir.getDeclaration(new_decl_inst);
3349 // Attempt to match this to a declaration in the old ZIR:
3350 // * For named declarations (`const`/`var`/`fn`), we match based on name.
3351 // * For named tests (`test "foo"`), we also match based on name.
3352 // * For unnamed tests and decltests, we match based on order.
3353 // * For comptime blocks, we match based on order.
3354 // * For usingnamespace decls, we match based on order.
3355 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
3356 const old_decl_inst = switch (new_decl.name) {
3357 .@"comptime" => inst: {
3358 if (comptime_decl_idx == comptime_decls.items.len) continue;
3359 defer comptime_decl_idx += 1;
3360 break :inst comptime_decls.items[comptime_decl_idx];
3361 },
3362 .@"usingnamespace" => inst: {
3363 if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue;
3364 defer usingnamespace_decl_idx += 1;
3365 break :inst usingnamespace_decls.items[usingnamespace_decl_idx];
3366 },
3367 .unnamed_test, .decltest => inst: {
3368 if (unnamed_test_idx == unnamed_tests.items.len) continue;
3369 defer unnamed_test_idx += 1;
3370 break :inst unnamed_tests.items[unnamed_test_idx];
3371 },
3372 _ => inst: {
3373 const name_nts = new_decl.name.toString(old_zir).?;
3374 const name = new_zir.nullTerminatedString(name_nts);
3375 if (new_decl.name.isNamedTest(new_zir)) {
3376 break :inst named_tests.get(name) orelse continue;
3377 } else {
3378 break :inst named_decls.get(name) orelse continue;
3379 }
3380 },
3381 };
3382
3383 // Match the `declaration` instruction
3384 try inst_map.put(gpa, old_decl_inst, new_decl_inst);
3385
3386 // Find namespace declarations within this declaration
3387 try old_zir.findDecls(&old_decls, old_decl_inst);
3388 try new_zir.findDecls(&new_decls, new_decl_inst);
3389
3390 // We don't have any smart way of matching up these namespace declarations, so we always
3391 // correlate them based on source order.
3392 const n = @min(old_decls.items.len, new_decls.items.len);
3393 try match_stack.ensureUnusedCapacity(gpa, n);
3394 for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| {
3395 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
3396 }
3397 }
3398 }
3399}
3400
3401/// Like `ensureDeclAnalyzed`, but the Decl is a file's root Decl.
3402pub fn ensureFileAnalyzed(zcu: *Zcu, file: *File) SemaError!void {
3403 if (file.root_decl.unwrap()) |existing_root| {
3404 return zcu.ensureDeclAnalyzed(existing_root);
3405 } else {
3406 return zcu.semaFile(file);
3407 }
3408}
3409
3410/// This ensures that the Decl will have an up-to-date Type and Value populated.
3411/// However the resolution status of the Type may not be fully resolved.
3412/// For example an inferred error set is not resolved until after `analyzeFnBody`.
3413/// is called.
3414pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
3415 const tracy = trace(@src());
3416 defer tracy.end();
3417
3418 const ip = &mod.intern_pool;
3419 const decl = mod.declPtr(decl_index);
3420
3421 log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{
3422 @intFromEnum(decl_index),
3423 decl.name.fmt(ip),
3424 });
3425
3426 // Determine whether or not this Decl is outdated, i.e. requires re-analysis
3427 // even if `complete`. If a Decl is PO, we pessismistically assume that it
3428 // *does* require re-analysis, to ensure that the Decl is definitely
3429 // up-to-date when this function returns.
3430
3431 // If analysis occurs in a poor order, this could result in over-analysis.
3432 // We do our best to avoid this by the other dependency logic in this file
3433 // which tries to limit re-analysis to Decls whose previously listed
3434 // dependencies are all up-to-date.
3435
3436 const decl_as_depender = InternPool.Depender.wrap(.{ .decl = decl_index });
3437 const decl_was_outdated = mod.outdated.swapRemove(decl_as_depender) or
3438 mod.potentially_outdated.swapRemove(decl_as_depender);
3439
3440 if (decl_was_outdated) {
3441 _ = mod.outdated_ready.swapRemove(decl_as_depender);
3442 }
3443
3444 const was_outdated = mod.outdated_file_root.swapRemove(decl_index) or decl_was_outdated;
3445
3446 switch (decl.analysis) {
3447 .in_progress => unreachable,
3448
3449 .file_failure => return error.AnalysisFail,
3450
3451 .sema_failure,
3452 .dependency_failure,
3453 .codegen_failure,
3454 => if (!was_outdated) return error.AnalysisFail,
3455
3456 .complete => if (!was_outdated) return,
3457
3458 .unreferenced => {},
3459 }
3460
3461 if (was_outdated) {
3462 // The exports this Decl performs will be re-discovered, so we remove them here
3463 // prior to re-analysis.
3464 if (build_options.only_c) unreachable;
3465 try mod.deleteDeclExports(decl_index);
3466 }
3467
3468 const sema_result: SemaDeclResult = blk: {
3469 if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) {
3470 // Anonymous decl. We don't semantically analyze these.
3471 break :blk .{
3472 .invalidate_decl_val = false,
3473 .invalidate_decl_ref = false,
3474 };
3475 }
3476
3477 if (mod.declIsRoot(decl_index)) {
3478 const changed = try mod.semaFileUpdate(decl.getFileScope(mod), decl_was_outdated);
3479 break :blk .{
3480 .invalidate_decl_val = changed,
3481 .invalidate_decl_ref = changed,
3482 };
3483 }
3484
3485 const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
3486 defer decl_prog_node.end();
3487
3488 break :blk mod.semaDecl(decl_index) catch |err| switch (err) {
3489 error.AnalysisFail => {
3490 if (decl.analysis == .in_progress) {
3491 // If this decl caused the compile error, the analysis field would
3492 // be changed to indicate it was this Decl's fault. Because this
3493 // did not happen, we infer here that it was a dependency failure.
3494 decl.analysis = .dependency_failure;
3495 }
3496 return error.AnalysisFail;
3497 },
3498 error.GenericPoison => unreachable,
3499 else => |e| {
3500 decl.analysis = .sema_failure;
3501 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
3502 try mod.retryable_failures.append(mod.gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
3503 mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
3504 mod.gpa,
3505 decl.navSrcLoc(mod).upgrade(mod),
3506 "unable to analyze: {s}",
3507 .{@errorName(e)},
3508 ));
3509 return error.AnalysisFail;
3510 },
3511 };
3512 };
3513
3514 // TODO: we do not yet have separate dependencies for decl values vs types.
3515 if (decl_was_outdated) {
3516 if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) {
3517 log.debug("Decl tv invalidated ('{d}')", .{@intFromEnum(decl_index)});
3518 // This dependency was marked as PO, meaning dependees were waiting
3519 // on its analysis result, and it has turned out to be outdated.
3520 // Update dependees accordingly.
3521 try mod.markDependeeOutdated(.{ .decl_val = decl_index });
3522 } else {
3523 log.debug("Decl tv up-to-date ('{d}')", .{@intFromEnum(decl_index)});
3524 // This dependency was previously PO, but turned out to be up-to-date.
3525 // We do not need to queue successive analysis.
3526 try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index });
3527 }
3528 }
3529}
3530
3531pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.Index) SemaError!void {
3532 const tracy = trace(@src());
3533 defer tracy.end();
3534
3535 const gpa = zcu.gpa;
3536 const ip = &zcu.intern_pool;
3537
3538 // We only care about the uncoerced function.
3539 // We need to do this for the "orphaned function" check below to be valid.
3540 const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
3541
3542 const func = zcu.funcInfo(maybe_coerced_func_index);
3543 const decl_index = func.owner_decl;
3544 const decl = zcu.declPtr(decl_index);
3545
3546 log.debug("ensureFuncBodyAnalyzed '{d}' (instance of '{}')", .{
3547 @intFromEnum(func_index),
3548 decl.name.fmt(ip),
3549 });
3550
3551 // First, our owner decl must be up-to-date. This will always be the case
3552 // during the first update, but may not on successive updates if we happen
3553 // to get analyzed before our parent decl.
3554 try zcu.ensureDeclAnalyzed(decl_index);
3555
3556 // On an update, it's possible this function changed such that our owner
3557 // decl now refers to a different function, making this one orphaned. If
3558 // that's the case, we should remove this function from the binary.
3559 if (decl.val.ip_index != func_index) {
3560 try zcu.markDependeeOutdated(.{ .func_ies = func_index });
3561 ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
3562 ip.remove(func_index);
3563 @panic("TODO: remove orphaned function from binary");
3564 }
3565
3566 // We'll want to remember what the IES used to be before the update for
3567 // dependency invalidation purposes.
3568 const old_resolved_ies = if (func.analysis(ip).inferred_error_set)
3569 func.resolvedErrorSet(ip).*
3570 else
3571 .none;
3572
3573 switch (decl.analysis) {
3574 .unreferenced => unreachable,
3575 .in_progress => unreachable,
3576
3577 .codegen_failure => unreachable, // functions do not perform constant value generation
3578
3579 .file_failure,
3580 .sema_failure,
3581 .dependency_failure,
3582 => return error.AnalysisFail,
3583
3584 .complete => {},
3585 }
3586
3587 const func_as_depender = InternPool.Depender.wrap(.{ .func = func_index });
3588 const was_outdated = zcu.outdated.swapRemove(func_as_depender) or
3589 zcu.potentially_outdated.swapRemove(func_as_depender);
3590
3591 if (was_outdated) {
3592 _ = zcu.outdated_ready.swapRemove(func_as_depender);
3593 }
3594
3595 switch (func.analysis(ip).state) {
3596 .success => if (!was_outdated) return,
3597 .sema_failure,
3598 .dependency_failure,
3599 .codegen_failure,
3600 => if (!was_outdated) return error.AnalysisFail,
3601 .none, .queued => {},
3602 .in_progress => unreachable,
3603 .inline_only => unreachable, // don't queue work for this
3604 }
3605
3606 log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{
3607 @intFromEnum(func_index),
3608 if (was_outdated) "outdated" else "never analyzed",
3609 });
3610
3611 var tmp_arena = std.heap.ArenaAllocator.init(gpa);
3612 defer tmp_arena.deinit();
3613 const sema_arena = tmp_arena.allocator();
3614
3615 var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) {
3616 error.AnalysisFail => {
3617 if (func.analysis(ip).state == .in_progress) {
3618 // If this decl caused the compile error, the analysis field would
3619 // be changed to indicate it was this Decl's fault. Because this
3620 // did not happen, we infer here that it was a dependency failure.
3621 func.analysis(ip).state = .dependency_failure;
3622 }
3623 return error.AnalysisFail;
3624 },
3625 error.OutOfMemory => return error.OutOfMemory,
3626 };
3627 defer air.deinit(gpa);
3628
3629 const invalidate_ies_deps = i: {
3630 if (!was_outdated) break :i false;
3631 if (!func.analysis(ip).inferred_error_set) break :i true;
3632 const new_resolved_ies = func.resolvedErrorSet(ip).*;
3633 break :i new_resolved_ies != old_resolved_ies;
3634 };
3635 if (invalidate_ies_deps) {
3636 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
3637 try zcu.markDependeeOutdated(.{ .func_ies = func_index });
3638 } else if (was_outdated) {
3639 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
3640 try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index });
3641 }
3642
3643 const comp = zcu.comp;
3644
3645 const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
3646 const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null);
3647
3648 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
3649 return;
3650 }
3651
3652 var liveness = try Liveness.analyze(gpa, air, ip);
3653 defer liveness.deinit(gpa);
3654
3655 if (dump_air) {
3656 const fqn = try decl.fullyQualifiedName(zcu);
3657 std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)});
3658 @import("print_air.zig").dump(zcu, air, liveness);
3659 std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)});
3660 }
3661
3662 if (std.debug.runtime_safety) {
3663 var verify = Liveness.Verify{
3664 .gpa = gpa,
3665 .air = air,
3666 .liveness = liveness,
3667 .intern_pool = ip,
3668 };
3669 defer verify.deinit();
3670
3671 verify.verify() catch |err| switch (err) {
3672 error.OutOfMemory => return error.OutOfMemory,
3673 else => {
3674 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
3675 zcu.failed_decls.putAssumeCapacityNoClobber(
3676 decl_index,
3677 try Module.ErrorMsg.create(
3678 gpa,
3679 decl.navSrcLoc(zcu).upgrade(zcu),
3680 "invalid liveness: {s}",
3681 .{@errorName(err)},
3682 ),
3683 );
3684 func.analysis(ip).state = .codegen_failure;
3685 return;
3686 },
3687 };
3688 }
3689
3690 const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(ip), 0);
3691 defer codegen_prog_node.end();
3692
3693 if (comp.bin_file) |lf| {
3694 lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
3695 error.OutOfMemory => return error.OutOfMemory,
3696 error.AnalysisFail => {
3697 func.analysis(ip).state = .codegen_failure;
3698 },
3699 else => {
3700 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
3701 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
3702 gpa,
3703 decl.navSrcLoc(zcu).upgrade(zcu),
3704 "unable to codegen: {s}",
3705 .{@errorName(err)},
3706 ));
3707 func.analysis(ip).state = .codegen_failure;
3708 try zcu.retryable_failures.append(zcu.gpa, InternPool.Depender.wrap(.{ .func = func_index }));
3709 },
3710 };
3711 } else if (zcu.llvm_object) |llvm_object| {
3712 if (build_options.only_c) unreachable;
3713 llvm_object.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
3714 error.OutOfMemory => return error.OutOfMemory,
3715 error.AnalysisFail => {
3716 func.analysis(ip).state = .codegen_failure;
3717 },
3718 };
3719 }
3720}
3721
3722/// Ensure this function's body is or will be analyzed and emitted. This should
3723/// be called whenever a potential runtime call of a function is seen.
3724///
3725/// The caller is responsible for ensuring the function decl itself is already
3726/// analyzed, and for ensuring it can exist at runtime (see
3727/// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body
3728/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
3729pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) !void {
3730 const ip = &mod.intern_pool;
3731 const func = mod.funcInfo(func_index);
3732 const decl_index = func.owner_decl;
3733 const decl = mod.declPtr(decl_index);
3734
3735 switch (decl.analysis) {
3736 .unreferenced => unreachable,
3737 .in_progress => unreachable,
3738
3739 .file_failure,
3740 .sema_failure,
3741 .codegen_failure,
3742 .dependency_failure,
3743 // Analysis of the function Decl itself failed, but we've already
3744 // emitted an error for that. The callee doesn't need the function to be
3745 // analyzed right now, so its analysis can safely continue.
3746 => return,
3747
3748 .complete => {},
3749 }
3750
3751 assert(decl.has_tv);
3752
3753 const func_as_depender = InternPool.Depender.wrap(.{ .func = func_index });
3754 const is_outdated = mod.outdated.contains(func_as_depender) or
3755 mod.potentially_outdated.contains(func_as_depender);
3756
3757 switch (func.analysis(ip).state) {
3758 .none => {},
3759 .queued => return,
3760 // As above, we don't need to forward errors here.
3761 .sema_failure,
3762 .dependency_failure,
3763 .codegen_failure,
3764 .success,
3765 => if (!is_outdated) return,
3766 .in_progress => return,
3767 .inline_only => unreachable, // don't queue work for this
3768 }
3769
3770 // Decl itself is safely analyzed, and body analysis is not yet queued
3771
3772 try mod.comp.work_queue.writeItem(.{ .codegen_func = func_index });
3773 if (mod.emit_h != null) {
3774 // TODO: we ideally only want to do this if the function's type changed
3775 // since the last update
3776 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
3777 }
3778 func.analysis(ip).state = .queued;
3779}
3780
3781/// https://github.com/ziglang/zig/issues/14307
3782pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void {
3783 const file = (try mod.importPkg(pkg)).file;
3784 if (file.root_decl == .none) {
3785 return mod.semaFile(file);
3786 }
3787}
3788
3789fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index {
3790 const gpa = zcu.gpa;
3791 const ip = &zcu.intern_pool;
3792 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
3793 assert(extended.opcode == .struct_decl);
3794 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3795 assert(!small.has_captures_len);
3796 assert(!small.has_backing_int);
3797 assert(small.layout == .auto);
3798 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
3799 const fields_len = if (small.has_fields_len) blk: {
3800 const fields_len = file.zir.extra[extra_index];
3801 extra_index += 1;
3802 break :blk fields_len;
3803 } else 0;
3804 const decls_len = if (small.has_decls_len) blk: {
3805 const decls_len = file.zir.extra[extra_index];
3806 extra_index += 1;
3807 break :blk decls_len;
3808 } else 0;
3809 const decls = file.zir.bodySlice(extra_index, decls_len);
3810 extra_index += decls_len;
3811
3812 const tracked_inst = try ip.trackZir(gpa, file, .main_struct_inst);
3813 const wip_ty = switch (try ip.getStructType(gpa, .{
3814 .layout = .auto,
3815 .fields_len = fields_len,
3816 .known_non_opv = small.known_non_opv,
3817 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
3818 .is_tuple = small.is_tuple,
3819 .any_comptime_fields = small.any_comptime_fields,
3820 .any_default_inits = small.any_default_inits,
3821 .inits_resolved = false,
3822 .any_aligned_fields = small.any_aligned_fields,
3823 .has_namespace = true,
3824 .key = .{ .declared = .{
3825 .zir_index = tracked_inst,
3826 .captures = &.{},
3827 } },
3828 })) {
3829 .existing => unreachable, // we wouldn't be analysing the file root if this type existed
3830 .wip => |wip| wip,
3831 };
3832 errdefer wip_ty.cancel(ip);
3833
3834 if (zcu.comp.debug_incremental) {
3835 try ip.addDependency(
3836 gpa,
3837 InternPool.Depender.wrap(.{ .decl = decl_index }),
3838 .{ .src_hash = tracked_inst },
3839 );
3840 }
3841
3842 const decl = zcu.declPtr(decl_index);
3843 decl.val = Value.fromInterned(wip_ty.index);
3844 decl.has_tv = true;
3845 decl.owns_tv = true;
3846 decl.analysis = .complete;
3847
3848 try zcu.scanNamespace(namespace_index, decls, decl);
3849
3850 return wip_ty.finish(ip, decl_index, namespace_index.toOptional());
3851}
3852
3853/// Re-analyze the root Decl of a file on an incremental update.
3854/// If `type_outdated`, the struct type itself is considered outdated and is
3855/// reconstructed at a new InternPool index. Otherwise, the namespace is just
3856/// re-analyzed. Returns whether the decl's tyval was invalidated.
3857fn semaFileUpdate(zcu: *Zcu, file: *File, type_outdated: bool) SemaError!bool {
3858 const decl = zcu.declPtr(file.root_decl.unwrap().?);
3859
3860 log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{
3861 file.mod.fully_qualified_name,
3862 file.sub_file_path,
3863 type_outdated,
3864 });
3865
3866 if (file.status != .success_zir) {
3867 if (decl.analysis == .file_failure) {
3868 return false;
3869 } else {
3870 decl.analysis = .file_failure;
3871 return true;
3872 }
3873 }
3874
3875 if (decl.analysis == .file_failure) {
3876 // No struct type currently exists. Create one!
3877 _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file);
3878 return true;
3879 }
3880
3881 assert(decl.has_tv);
3882 assert(decl.owns_tv);
3883
3884 if (type_outdated) {
3885 // Invalidate the existing type, reusing the decl and namespace.
3886 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = file.root_decl.unwrap().? }));
3887 zcu.intern_pool.remove(decl.val.toIntern());
3888 decl.val = undefined;
3889 _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file);
3890 return true;
3891 }
3892
3893 // Only the struct's namespace is outdated.
3894 // Preserve the type - just scan the namespace again.
3895
3896 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
3897 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
3898
3899 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
3900 extra_index += @intFromBool(small.has_fields_len);
3901 const decls_len = if (small.has_decls_len) blk: {
3902 const decls_len = file.zir.extra[extra_index];
3903 extra_index += 1;
3904 break :blk decls_len;
3905 } else 0;
3906 const decls = file.zir.bodySlice(extra_index, decls_len);
3907
3908 if (!type_outdated) {
3909 try zcu.scanNamespace(decl.src_namespace, decls, decl);
3910 }
3911
3912 return false;
3913}
3914
3915/// Regardless of the file status, will create a `Decl` if none exists so that we can track
3916/// dependencies and re-analyze when the file becomes outdated.
3917fn semaFile(mod: *Module, file: *File) SemaError!void {
3918 const tracy = trace(@src());
3919 defer tracy.end();
3920
3921 assert(file.root_decl == .none);
3922
3923 const gpa = mod.gpa;
3924 log.debug("semaFile mod={s} sub_file_path={s}", .{
3925 file.mod.fully_qualified_name, file.sub_file_path,
3926 });
3927
3928 // Because these three things each reference each other, `undefined`
3929 // placeholders are used before being set after the struct type gains an
3930 // InternPool index.
3931 const new_namespace_index = try mod.createNamespace(.{
3932 .parent = .none,
3933 .decl_index = undefined,
3934 .file_scope = file,
3935 });
3936 errdefer mod.destroyNamespace(new_namespace_index);
3937
3938 const new_decl_index = try mod.allocateNewDecl(new_namespace_index);
3939 const new_decl = mod.declPtr(new_decl_index);
3940 errdefer @panic("TODO error handling");
3941
3942 file.root_decl = new_decl_index.toOptional();
3943 mod.namespacePtr(new_namespace_index).decl_index = new_decl_index;
3944
3945 new_decl.name = try file.fullyQualifiedName(mod);
3946 new_decl.name_fully_qualified = true;
3947 new_decl.src_line = 0;
3948 new_decl.is_pub = true;
3949 new_decl.is_exported = false;
3950 new_decl.alignment = .none;
3951 new_decl.@"linksection" = .none;
3952 new_decl.analysis = .in_progress;
3953
3954 if (file.status != .success_zir) {
3955 new_decl.analysis = .file_failure;
3956 return;
3957 }
3958 assert(file.zir_loaded);
3959
3960 const struct_ty = try mod.getFileRootStruct(new_decl_index, new_namespace_index, file);
3961 errdefer mod.intern_pool.remove(struct_ty);
3962
3963 switch (mod.comp.cache_use) {
3964 .whole => |whole| if (whole.cache_manifest) |man| {
3965 const source = file.getSource(gpa) catch |err| {
3966 try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)});
3967 return error.AnalysisFail;
3968 };
3969
3970 const resolved_path = std.fs.path.resolve(gpa, &.{
3971 file.mod.root.root_dir.path orelse ".",
3972 file.mod.root.sub_path,
3973 file.sub_file_path,
3974 }) catch |err| {
3975 try reportRetryableFileError(mod, file, "unable to resolve path: {s}", .{@errorName(err)});
3976 return error.AnalysisFail;
3977 };
3978 errdefer gpa.free(resolved_path);
3979
3980 whole.cache_manifest_mutex.lock();
3981 defer whole.cache_manifest_mutex.unlock();
3982 try man.addFilePostContents(resolved_path, source.bytes, source.stat);
3983 },
3984 .incremental => {},
3985 }
3986}
3987
3988const SemaDeclResult = packed struct {
3989 /// Whether the value of a `decl_val` of this Decl changed.
3990 invalidate_decl_val: bool,
3991 /// Whether the type of a `decl_ref` of this Decl changed.
3992 invalidate_decl_ref: bool,
3993};
3994
3995fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
3996 const tracy = trace(@src());
3997 defer tracy.end();
3998
3999 const decl = mod.declPtr(decl_index);
4000 const ip = &mod.intern_pool;
4001
4002 if (decl.getFileScope(mod).status != .success_zir) {
4003 return error.AnalysisFail;
4004 }
4005
4006 assert(!mod.declIsRoot(decl_index));
4007
4008 if (decl.zir_decl_index == .none and decl.owns_tv) {
4009 // We are re-analyzing an anonymous owner Decl (for a function or a namespace type).
4010 return mod.semaAnonOwnerDecl(decl_index);
4011 }
4012
4013 log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)});
4014 log.debug("decl name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)});
4015 defer blk: {
4016 log.debug("finish decl name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});
4017 }
4018
4019 const old_has_tv = decl.has_tv;
4020 // The following values are ignored if `!old_has_tv`
4021 const old_ty = if (old_has_tv) decl.typeOf(mod) else undefined;
4022 const old_val = decl.val;
4023 const old_align = decl.alignment;
4024 const old_linksection = decl.@"linksection";
4025 const old_addrspace = decl.@"addrspace";
4026 const old_is_inline = if (decl.getOwnedFunction(mod)) |prev_func|
4027 prev_func.analysis(ip).state == .inline_only
4028 else
4029 false;
4030
4031 const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip);
4032
4033 const gpa = mod.gpa;
4034 const zir = decl.getFileScope(mod).zir;
4035
4036 const builtin_type_target_index: InternPool.Index = ip_index: {
4037 const std_mod = mod.std_mod;
4038 if (decl.getFileScope(mod).mod != std_mod) break :ip_index .none;
4039 // We're in the std module.
4040 const std_file = (try mod.importPkg(std_mod)).file;
4041 const std_decl = mod.declPtr(std_file.root_decl.unwrap().?);
4042 const std_namespace = std_decl.getInnerNamespace(mod).?;
4043 const builtin_str = try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls);
4044 const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .zcu = mod }) orelse break :ip_index .none);
4045 const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :ip_index .none;
4046 if (decl.src_namespace != builtin_namespace) break :ip_index .none;
4047 // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index.
4048 for ([_][]const u8{
4049 "AtomicOrder",
4050 "AtomicRmwOp",
4051 "CallingConvention",
4052 "AddressSpace",
4053 "FloatMode",
4054 "ReduceOp",
4055 "CallModifier",
4056 "PrefetchOptions",
4057 "ExportOptions",
4058 "ExternOptions",
4059 "Type",
4060 }, [_]InternPool.Index{
4061 .atomic_order_type,
4062 .atomic_rmw_op_type,
4063 .calling_convention_type,
4064 .address_space_type,
4065 .float_mode_type,
4066 .reduce_op_type,
4067 .call_modifier_type,
4068 .prefetch_options_type,
4069 .export_options_type,
4070 .extern_options_type,
4071 .type_info_type,
4072 }) |type_name, type_ip| {
4073 if (decl.name.eqlSlice(type_name, ip)) break :ip_index type_ip;
4074 }
4075 break :ip_index .none;
4076 };
4077
4078 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
4079
4080 decl.analysis = .in_progress;
4081
4082 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
4083 defer analysis_arena.deinit();
4084
4085 var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
4086 defer comptime_err_ret_trace.deinit();
4087
4088 var sema: Sema = .{
4089 .mod = mod,
4090 .gpa = gpa,
4091 .arena = analysis_arena.allocator(),
4092 .code = zir,
4093 .owner_decl = decl,
4094 .owner_decl_index = decl_index,
4095 .func_index = .none,
4096 .func_is_naked = false,
4097 .fn_ret_ty = Type.void,
4098 .fn_ret_ty_ies = null,
4099 .owner_func_index = .none,
4100 .comptime_err_ret_trace = &comptime_err_ret_trace,
4101 .builtin_type_target_index = builtin_type_target_index,
4102 };
4103 defer sema.deinit();
4104
4105 // Every Decl (other than file root Decls, which do not have a ZIR index) has a dependency on its own source.
4106 try sema.declareDependency(.{ .src_hash = try ip.trackZir(
4107 sema.gpa,
4108 decl.getFileScope(mod),
4109 decl_inst,
4110 ) });
4111
4112 var block_scope: Sema.Block = .{
4113 .parent = null,
4114 .sema = &sema,
4115 .namespace = decl.src_namespace,
4116 .instructions = .{},
4117 .inlining = null,
4118 .is_comptime = true,
4119 .src_base_inst = decl.zir_decl_index.unwrap().?,
4120 .type_name_ctx = decl.name,
4121 };
4122 defer block_scope.instructions.deinit(gpa);
4123
4124 const decl_bodies = decl.zirBodies(mod);
4125
4126 const result_ref = try sema.resolveInlineBody(&block_scope, decl_bodies.value_body, decl_inst);
4127 // We'll do some other bits with the Sema. Clear the type target index just
4128 // in case they analyze any type.
4129 sema.builtin_type_target_index = .none;
4130 const align_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_align = 0 });
4131 const section_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_section = 0 });
4132 const address_space_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_addrspace = 0 });
4133 const ty_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_ty = 0 });
4134 const init_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_init = 0 });
4135 const decl_val = try sema.resolveFinalDeclValue(&block_scope, init_src, result_ref);
4136 const decl_ty = decl_val.typeOf(mod);
4137
4138 // Note this resolves the type of the Decl, not the value; if this Decl
4139 // is a struct, for example, this resolves `type` (which needs no resolution),
4140 // not the struct itself.
4141 try sema.resolveTypeLayout(decl_ty);
4142
4143 if (decl.kind == .@"usingnamespace") {
4144 if (!decl_ty.eql(Type.type, mod)) {
4145 return sema.fail(&block_scope, ty_src, "expected type, found {}", .{
4146 decl_ty.fmt(mod),
4147 });
4148 }
4149 const ty = decl_val.toType();
4150 if (ty.getNamespace(mod) == null) {
4151 return sema.fail(&block_scope, ty_src, "type {} has no namespace", .{ty.fmt(mod)});
4152 }
4153
4154 decl.val = ty.toValue();
4155 decl.alignment = .none;
4156 decl.@"linksection" = .none;
4157 decl.has_tv = true;
4158 decl.owns_tv = false;
4159 decl.analysis = .complete;
4160
4161 // TODO: usingnamespace cannot currently participate in incremental compilation
4162 return .{
4163 .invalidate_decl_val = true,
4164 .invalidate_decl_ref = true,
4165 };
4166 }
4167
4168 var queue_linker_work = true;
4169 var is_func = false;
4170 var is_inline = false;
4171 switch (decl_val.toIntern()) {
4172 .generic_poison => unreachable,
4173 .unreachable_value => unreachable,
4174 else => switch (ip.indexToKey(decl_val.toIntern())) {
4175 .variable => |variable| {
4176 decl.owns_tv = variable.decl == decl_index;
4177 queue_linker_work = decl.owns_tv;
4178 },
4179
4180 .extern_func => |extern_func| {
4181 decl.owns_tv = extern_func.decl == decl_index;
4182 queue_linker_work = decl.owns_tv;
4183 is_func = decl.owns_tv;
4184 },
4185
4186 .func => |func| {
4187 decl.owns_tv = func.owner_decl == decl_index;
4188 queue_linker_work = false;
4189 is_inline = decl.owns_tv and decl_ty.fnCallingConvention(mod) == .Inline;
4190 is_func = decl.owns_tv;
4191 },
4192
4193 else => {},
4194 },
4195 }
4196
4197 decl.val = decl_val;
4198 // Function linksection, align, and addrspace were already set by Sema
4199 if (!is_func) {
4200 decl.alignment = blk: {
4201 const align_body = decl_bodies.align_body orelse break :blk .none;
4202 const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst);
4203 break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
4204 };
4205 decl.@"linksection" = blk: {
4206 const linksection_body = decl_bodies.linksection_body orelse break :blk .none;
4207 const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst);
4208 const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{
4209 .needed_comptime_reason = "linksection must be comptime-known",
4210 });
4211 if (mem.indexOfScalar(u8, bytes, 0) != null) {
4212 return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{});
4213 } else if (bytes.len == 0) {
4214 return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
4215 }
4216 break :blk try ip.getOrPutStringOpt(gpa, bytes, .no_embedded_nulls);
4217 };
4218 decl.@"addrspace" = blk: {
4219 const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_val.toIntern())) {
4220 .variable => .variable,
4221 .extern_func, .func => .function,
4222 else => .constant,
4223 };
4224
4225 const target = sema.mod.getTarget();
4226
4227 const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) {
4228 .function => target_util.defaultAddressSpace(target, .function),
4229 .variable => target_util.defaultAddressSpace(target, .global_mutable),
4230 .constant => target_util.defaultAddressSpace(target, .global_constant),
4231 else => unreachable,
4232 };
4233 const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst);
4234 break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx);
4235 };
4236 }
4237 decl.has_tv = true;
4238 decl.analysis = .complete;
4239
4240 const result: SemaDeclResult = if (old_has_tv) .{
4241 .invalidate_decl_val = !decl_ty.eql(old_ty, mod) or
4242 !decl.val.eql(old_val, decl_ty, mod) or
4243 is_inline != old_is_inline,
4244 .invalidate_decl_ref = !decl_ty.eql(old_ty, mod) or
4245 decl.alignment != old_align or
4246 decl.@"linksection" != old_linksection or
4247 decl.@"addrspace" != old_addrspace or
4248 is_inline != old_is_inline,
4249 } else .{
4250 .invalidate_decl_val = true,
4251 .invalidate_decl_ref = true,
4252 };
4253
4254 const has_runtime_bits = queue_linker_work and (is_func or try sema.typeHasRuntimeBits(decl_ty));
4255 if (has_runtime_bits) {
4256 // Needed for codegen_decl which will call updateDecl and then the
4257 // codegen backend wants full access to the Decl Type.
4258 try sema.resolveTypeFully(decl_ty);
4259
4260 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
4261
4262 if (result.invalidate_decl_ref and mod.emit_h != null) {
4263 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
4264 }
4265 }
4266
4267 if (decl.is_exported) {
4268 const export_src: LazySrcLoc = block_scope.src(.{ .token_offset = @intFromBool(decl.is_pub) });
4269 if (is_inline) return sema.fail(&block_scope, export_src, "export of inline function", .{});
4270 // The scope needs to have the decl in it.
4271 try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index);
4272 }
4273
4274 return result;
4275}
4276
4277fn semaAnonOwnerDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult {
4278 const decl = zcu.declPtr(decl_index);
4279
4280 assert(decl.has_tv);
4281 assert(decl.owns_tv);
4282
4283 log.debug("semaAnonOwnerDecl '{d}'", .{@intFromEnum(decl_index)});
4284
4285 switch (decl.typeOf(zcu).zigTypeTag(zcu)) {
4286 .Fn => @panic("TODO: update fn instance"),
4287 .Type => {},
4288 else => unreachable,
4289 }
4290
4291 // We are the owner Decl of a type, and we were marked as outdated. That means the *structure*
4292 // of this type changed; not just its namespace. Therefore, we need a new InternPool index.
4293 //
4294 // However, as soon as we make that, the context that created us will require re-analysis anyway
4295 // (as it depends on this Decl's value), meaning the `struct_decl` (or equivalent) instruction
4296 // will be analyzed again. Since Sema already needs to be able to reconstruct types like this,
4297 // why should we bother implementing it here too when the Sema logic will be hit right after?
4298 //
4299 // So instead, let's just mark this Decl as failed - so that any remaining Decls which genuinely
4300 // reference it (via `@This`) end up silently erroring too - and we'll let Sema make a new type
4301 // with a new Decl.
4302 //
4303 // Yes, this does mean that any type owner Decl has a constant value for its entire lifetime.
4304 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
4305 zcu.intern_pool.remove(decl.val.toIntern());
4306 decl.analysis = .dependency_failure;
4307 return .{
4308 .invalidate_decl_val = true,
4309 .invalidate_decl_ref = true,
4310 };
4311}
4312
4313pub const ImportFileResult = struct {
4314 file: *File,
4315 is_new: bool,
4316 is_pkg: bool,
4317};
4318
4319pub fn importPkg(zcu: *Zcu, mod: *Package.Module) !ImportFileResult {
4320 const gpa = zcu.gpa;
4321
4322 // The resolved path is used as the key in the import table, to detect if
4323 // an import refers to the same as another, despite different relative paths
4324 // or differently mapped package names.
4325 const resolved_path = try std.fs.path.resolve(gpa, &.{
4326 mod.root.root_dir.path orelse ".",
4327 mod.root.sub_path,
4328 mod.root_src_path,
4329 });
4330 var keep_resolved_path = false;
4331 defer if (!keep_resolved_path) gpa.free(resolved_path);
4332
4333 const gop = try zcu.import_table.getOrPut(gpa, resolved_path);
4334 errdefer _ = zcu.import_table.pop();
4335 if (gop.found_existing) {
4336 try gop.value_ptr.*.addReference(zcu.*, .{ .root = mod });
4337 return ImportFileResult{
4338 .file = gop.value_ptr.*,
4339 .is_new = false,
4340 .is_pkg = true,
4341 };
4342 }
4343
4344 if (mod.builtin_file) |builtin_file| {
4345 keep_resolved_path = true; // It's now owned by import_table.
4346 gop.value_ptr.* = builtin_file;
4347 try builtin_file.addReference(zcu.*, .{ .root = mod });
4348 try zcu.path_digest_map.put(gpa, builtin_file.path_digest, {});
4349 return .{
4350 .file = builtin_file,
4351 .is_new = false,
4352 .is_pkg = true,
4353 };
4354 }
4355
4356 const sub_file_path = try gpa.dupe(u8, mod.root_src_path);
4357 errdefer gpa.free(sub_file_path);
4358
4359 const new_file = try gpa.create(File);
4360 errdefer gpa.destroy(new_file);
4361
4362 keep_resolved_path = true; // It's now owned by import_table.
4363 gop.value_ptr.* = new_file;
4364 new_file.* = .{
4365 .sub_file_path = sub_file_path,
4366 .source = undefined,
4367 .source_loaded = false,
4368 .tree_loaded = false,
4369 .zir_loaded = false,
4370 .stat = undefined,
4371 .tree = undefined,
4372 .zir = undefined,
4373 .status = .never_loaded,
4374 .mod = mod,
4375 .root_decl = .none,
4376 .path_digest = digest: {
4377 const want_local_cache = mod == zcu.main_mod;
4378 var path_hash: Cache.HashHelper = .{};
4379 path_hash.addBytes(build_options.version);
4380 path_hash.add(builtin.zig_backend);
4381 if (!want_local_cache) {
4382 path_hash.addOptionalBytes(mod.root.root_dir.path);
4383 path_hash.addBytes(mod.root.sub_path);
4384 }
4385 path_hash.addBytes(sub_file_path);
4386 var bin: Cache.BinDigest = undefined;
4387 path_hash.hasher.final(&bin);
4388 break :digest bin;
4389 },
4390 };
4391 try new_file.addReference(zcu.*, .{ .root = mod });
4392 try zcu.path_digest_map.put(gpa, new_file.path_digest, {});
4393 return ImportFileResult{
4394 .file = new_file,
4395 .is_new = true,
4396 .is_pkg = true,
4397 };
4398}
4399
4400pub fn importFile(
4401 zcu: *Zcu,
4402 cur_file: *File,
4403 import_string: []const u8,
4404) !ImportFileResult {
4405 if (std.mem.eql(u8, import_string, "std")) {
4406 return zcu.importPkg(zcu.std_mod);
4407 }
4408 if (std.mem.eql(u8, import_string, "root")) {
4409 return zcu.importPkg(zcu.root_mod);
4410 }
4411 if (cur_file.mod.deps.get(import_string)) |pkg| {
4412 return zcu.importPkg(pkg);
4413 }
4414 if (!mem.endsWith(u8, import_string, ".zig")) {
4415 return error.ModuleNotFound;
4416 }
4417 const gpa = zcu.gpa;
4418
4419 // The resolved path is used as the key in the import table, to detect if
4420 // an import refers to the same as another, despite different relative paths
4421 // or differently mapped package names.
4422 const resolved_path = try std.fs.path.resolve(gpa, &.{
4423 cur_file.mod.root.root_dir.path orelse ".",
4424 cur_file.mod.root.sub_path,
4425 cur_file.sub_file_path,
4426 "..",
4427 import_string,
4428 });
4429
4430 var keep_resolved_path = false;
4431 defer if (!keep_resolved_path) gpa.free(resolved_path);
4432
4433 const gop = try zcu.import_table.getOrPut(gpa, resolved_path);
4434 errdefer _ = zcu.import_table.pop();
4435 if (gop.found_existing) return ImportFileResult{
4436 .file = gop.value_ptr.*,
4437 .is_new = false,
4438 .is_pkg = false,
4439 };
4440
4441 const new_file = try gpa.create(File);
4442 errdefer gpa.destroy(new_file);
4443
4444 const resolved_root_path = try std.fs.path.resolve(gpa, &.{
4445 cur_file.mod.root.root_dir.path orelse ".",
4446 cur_file.mod.root.sub_path,
4447 });
4448 defer gpa.free(resolved_root_path);
4449
4450 const sub_file_path = p: {
4451 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
4452 errdefer gpa.free(relative);
4453
4454 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
4455 break :p relative;
4456 }
4457 return error.ImportOutsideModulePath;
4458 };
4459 errdefer gpa.free(sub_file_path);
4460
4461 log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{
4462 resolved_root_path, resolved_path, sub_file_path, import_string,
4463 });
4464
4465 keep_resolved_path = true; // It's now owned by import_table.
4466 gop.value_ptr.* = new_file;
4467 new_file.* = .{
4468 .sub_file_path = sub_file_path,
4469 .source = undefined,
4470 .source_loaded = false,
4471 .tree_loaded = false,
4472 .zir_loaded = false,
4473 .stat = undefined,
4474 .tree = undefined,
4475 .zir = undefined,
4476 .status = .never_loaded,
4477 .mod = cur_file.mod,
4478 .root_decl = .none,
4479 .path_digest = digest: {
4480 const want_local_cache = cur_file.mod == zcu.main_mod;
4481 var path_hash: Cache.HashHelper = .{};
4482 path_hash.addBytes(build_options.version);
4483 path_hash.add(builtin.zig_backend);
4484 if (!want_local_cache) {
4485 path_hash.addOptionalBytes(cur_file.mod.root.root_dir.path);
4486 path_hash.addBytes(cur_file.mod.root.sub_path);
4487 }
4488 path_hash.addBytes(sub_file_path);
4489 var bin: Cache.BinDigest = undefined;
4490 path_hash.hasher.final(&bin);
4491 break :digest bin;
4492 },
4493 };
4494 try zcu.path_digest_map.put(gpa, new_file.path_digest, {});
4495 return ImportFileResult{
4496 .file = new_file,
4497 .is_new = true,
4498 .is_pkg = false,
4499 };
4500}
4501
4502pub fn embedFile(
4503 mod: *Module,
4504 cur_file: *File,
4505 import_string: []const u8,
4506 src_loc: SrcLoc,
4507) !InternPool.Index {
4508 const gpa = mod.gpa;
4509
4510 if (cur_file.mod.deps.get(import_string)) |pkg| {
4511 const resolved_path = try std.fs.path.resolve(gpa, &.{
4512 pkg.root.root_dir.path orelse ".",
4513 pkg.root.sub_path,
4514 pkg.root_src_path,
4515 });
4516 var keep_resolved_path = false;
4517 defer if (!keep_resolved_path) gpa.free(resolved_path);
4518
4519 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4520 errdefer {
4521 assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path));
4522 keep_resolved_path = false;
4523 }
4524 if (gop.found_existing) return gop.value_ptr.*.val;
4525 keep_resolved_path = true;
4526
4527 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
4528 errdefer gpa.free(sub_file_path);
4529
4530 return newEmbedFile(mod, pkg, sub_file_path, resolved_path, gop.value_ptr, src_loc);
4531 }
4532
4533 // The resolved path is used as the key in the table, to detect if a file
4534 // refers to the same as another, despite different relative paths.
4535 const resolved_path = try std.fs.path.resolve(gpa, &.{
4536 cur_file.mod.root.root_dir.path orelse ".",
4537 cur_file.mod.root.sub_path,
4538 cur_file.sub_file_path,
4539 "..",
4540 import_string,
4541 });
4542
4543 var keep_resolved_path = false;
4544 defer if (!keep_resolved_path) gpa.free(resolved_path);
4545
4546 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4547 errdefer {
4548 assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path));
4549 keep_resolved_path = false;
4550 }
4551 if (gop.found_existing) return gop.value_ptr.*.val;
4552 keep_resolved_path = true;
4553
4554 const resolved_root_path = try std.fs.path.resolve(gpa, &.{
4555 cur_file.mod.root.root_dir.path orelse ".",
4556 cur_file.mod.root.sub_path,
4557 });
4558 defer gpa.free(resolved_root_path);
4559
4560 const sub_file_path = p: {
4561 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
4562 errdefer gpa.free(relative);
4563
4564 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
4565 break :p relative;
4566 }
4567 return error.ImportOutsideModulePath;
4568 };
4569 defer gpa.free(sub_file_path);
4570
4571 return newEmbedFile(mod, cur_file.mod, sub_file_path, resolved_path, gop.value_ptr, src_loc);
4572}
4573
4574/// https://github.com/ziglang/zig/issues/14307
4575fn newEmbedFile(
4576 mod: *Module,
4577 pkg: *Package.Module,
4578 sub_file_path: []const u8,
4579 resolved_path: []const u8,
4580 result: **EmbedFile,
4581 src_loc: SrcLoc,
4582) !InternPool.Index {
4583 const gpa = mod.gpa;
4584 const ip = &mod.intern_pool;
4585
4586 const new_file = try gpa.create(EmbedFile);
4587 errdefer gpa.destroy(new_file);
4588
4589 var file = try pkg.root.openFile(sub_file_path, .{});
4590 defer file.close();
4591
4592 const actual_stat = try file.stat();
4593 const stat: Cache.File.Stat = .{
4594 .size = actual_stat.size,
4595 .inode = actual_stat.inode,
4596 .mtime = actual_stat.mtime,
4597 };
4598 const size = std.math.cast(usize, actual_stat.size) orelse return error.Overflow;
4599
4600 const bytes = try ip.string_bytes.addManyAsSlice(gpa, try std.math.add(usize, size, 1));
4601 const actual_read = try file.readAll(bytes[0..size]);
4602 if (actual_read != size) return error.UnexpectedEndOfFile;
4603 bytes[size] = 0;
4604
4605 const comp = mod.comp;
4606 switch (comp.cache_use) {
4607 .whole => |whole| if (whole.cache_manifest) |man| {
4608 const copied_resolved_path = try gpa.dupe(u8, resolved_path);
4609 errdefer gpa.free(copied_resolved_path);
4610 whole.cache_manifest_mutex.lock();
4611 defer whole.cache_manifest_mutex.unlock();
4612 try man.addFilePostContents(copied_resolved_path, bytes[0..size], stat);
4613 },
4614 .incremental => {},
4615 }
4616
4617 const array_ty = try ip.get(gpa, .{ .array_type = .{
4618 .len = size,
4619 .sentinel = .zero_u8,
4620 .child = .u8_type,
4621 } });
4622 const array_val = try ip.get(gpa, .{ .aggregate = .{
4623 .ty = array_ty,
4624 .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, bytes.len, .maybe_embedded_nulls) },
4625 } });
4626
4627 const ptr_ty = (try mod.ptrType(.{
4628 .child = array_ty,
4629 .flags = .{
4630 .alignment = .none,
4631 .is_const = true,
4632 .address_space = .generic,
4633 },
4634 })).toIntern();
4635 const ptr_val = try ip.get(gpa, .{ .ptr = .{
4636 .ty = ptr_ty,
4637 .base_addr = .{ .anon_decl = .{
4638 .val = array_val,
4639 .orig_ty = ptr_ty,
4640 } },
4641 .byte_offset = 0,
4642 } });
4643
4644 result.* = new_file;
4645 new_file.* = .{
4646 .sub_file_path = try ip.getOrPutString(gpa, sub_file_path, .no_embedded_nulls),
4647 .owner = pkg,
4648 .stat = stat,
4649 .val = ptr_val,
4650 .src_loc = src_loc,
4651 };
4652 return ptr_val;
4653}
4654
4655pub fn scanNamespace(
4656 zcu: *Zcu,
4657 namespace_index: Namespace.Index,
4658 decls: []const Zir.Inst.Index,
4659 parent_decl: *Decl,
4660) Allocator.Error!void {
4661 const tracy = trace(@src());
4662 defer tracy.end();
4663
4664 const gpa = zcu.gpa;
4665 const namespace = zcu.namespacePtr(namespace_index);
4666
4667 // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather
4668 // than their name. We'll build an efficient mapping now, then discard the current `decls`.
4669 var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index) = .{};
4670 defer existing_by_inst.deinit(gpa);
4671
4672 try existing_by_inst.ensureTotalCapacity(gpa, @intCast(namespace.decls.count()));
4673
4674 for (namespace.decls.keys()) |decl_index| {
4675 const decl = zcu.declPtr(decl_index);
4676 existing_by_inst.putAssumeCapacityNoClobber(decl.zir_decl_index.unwrap().?, decl_index);
4677 }
4678
4679 var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{};
4680 defer seen_decls.deinit(gpa);
4681
4682 try zcu.comp.work_queue.ensureUnusedCapacity(decls.len);
4683
4684 namespace.decls.clearRetainingCapacity();
4685 try namespace.decls.ensureTotalCapacity(gpa, decls.len);
4686
4687 namespace.usingnamespace_set.clearRetainingCapacity();
4688
4689 var scan_decl_iter: ScanDeclIter = .{
4690 .zcu = zcu,
4691 .namespace_index = namespace_index,
4692 .parent_decl = parent_decl,
4693 .seen_decls = &seen_decls,
4694 .existing_by_inst = &existing_by_inst,
4695 .pass = .named,
4696 };
4697 for (decls) |decl_inst| {
4698 try scanDecl(&scan_decl_iter, decl_inst);
4699 }
4700 scan_decl_iter.pass = .unnamed;
4701 for (decls) |decl_inst| {
4702 try scanDecl(&scan_decl_iter, decl_inst);
4703 }
4704
4705 if (seen_decls.count() != namespace.decls.count()) {
4706 // Do a pass over the namespace contents and remove any decls from the last update
4707 // which were removed in this one.
4708 var i: usize = 0;
4709 while (i < namespace.decls.count()) {
4710 const decl_index = namespace.decls.keys()[i];
4711 const decl = zcu.declPtr(decl_index);
4712 if (!seen_decls.contains(decl.name)) {
4713 // We must preserve namespace ordering for @typeInfo.
4714 namespace.decls.orderedRemoveAt(i);
4715 i -= 1;
4716 }
4717 }
4718 }
4719}
4720
4721const ScanDeclIter = struct {
4722 zcu: *Zcu,
4723 namespace_index: Namespace.Index,
4724 parent_decl: *Decl,
4725 seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
4726 existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index),
4727 /// Decl scanning is run in two passes, so that we can detect when a generated
4728 /// name would clash with an explicit name and use a different one.
4729 pass: enum { named, unnamed },
4730 usingnamespace_index: usize = 0,
4731 comptime_index: usize = 0,
4732 unnamed_test_index: usize = 0,
4733
4734 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString {
4735 const zcu = iter.zcu;
4736 const gpa = zcu.gpa;
4737 const ip = &zcu.intern_pool;
4738 var name = try ip.getOrPutStringFmt(gpa, fmt, args, .no_embedded_nulls);
4739 var gop = try iter.seen_decls.getOrPut(gpa, name);
4740 var next_suffix: u32 = 0;
4741 while (gop.found_existing) {
4742 name = try ip.getOrPutStringFmt(gpa, "{}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls);
4743 gop = try iter.seen_decls.getOrPut(gpa, name);
4744 next_suffix += 1;
4745 }
4746 return name;
4747 }
4748};
4749
4750fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void {
4751 const tracy = trace(@src());
4752 defer tracy.end();
4753
4754 const zcu = iter.zcu;
4755 const namespace_index = iter.namespace_index;
4756 const namespace = zcu.namespacePtr(namespace_index);
4757 const gpa = zcu.gpa;
4758 const zir = namespace.file_scope.zir;
4759 const ip = &zcu.intern_pool;
4760
4761 const inst_data = zir.instructions.items(.data)[@intFromEnum(decl_inst)].declaration;
4762 const extra = zir.extraData(Zir.Inst.Declaration, inst_data.payload_index);
4763 const declaration = extra.data;
4764
4765 const line = iter.parent_decl.src_line + declaration.line_offset;
4766
4767 // Every Decl needs a name.
4768 const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) {
4769 .@"comptime" => info: {
4770 if (iter.pass != .unnamed) return;
4771 const i = iter.comptime_index;
4772 iter.comptime_index += 1;
4773 break :info .{
4774 try iter.avoidNameConflict("comptime_{d}", .{i}),
4775 .@"comptime",
4776 false,
4777 };
4778 },
4779 .@"usingnamespace" => info: {
4780 // TODO: this isn't right! These should be considered unnamed. Name conflicts can happen here.
4781 // The problem is, we need to preserve the decl ordering for `@typeInfo`.
4782 // I'm not bothering to fix this now, since some upcoming changes will change this code significantly anyway.
4783 if (iter.pass != .named) return;
4784 const i = iter.usingnamespace_index;
4785 iter.usingnamespace_index += 1;
4786 break :info .{
4787 try iter.avoidNameConflict("usingnamespace_{d}", .{i}),
4788 .@"usingnamespace",
4789 false,
4790 };
4791 },
4792 .unnamed_test => info: {
4793 if (iter.pass != .unnamed) return;
4794 const i = iter.unnamed_test_index;
4795 iter.unnamed_test_index += 1;
4796 break :info .{
4797 try iter.avoidNameConflict("test_{d}", .{i}),
4798 .@"test",
4799 false,
4800 };
4801 },
4802 .decltest => info: {
4803 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
4804 if (iter.pass != .unnamed) return;
4805 assert(declaration.flags.has_doc_comment);
4806 const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end]));
4807 break :info .{
4808 try iter.avoidNameConflict("decltest.{s}", .{name}),
4809 .@"test",
4810 true,
4811 };
4812 },
4813 _ => if (declaration.name.isNamedTest(zir)) info: {
4814 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
4815 if (iter.pass != .unnamed) return;
4816 break :info .{
4817 try iter.avoidNameConflict("test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}),
4818 .@"test",
4819 true,
4820 };
4821 } else info: {
4822 if (iter.pass != .named) return;
4823 const name = try ip.getOrPutString(
4824 gpa,
4825 zir.nullTerminatedString(declaration.name.toString(zir).?),
4826 .no_embedded_nulls,
4827 );
4828 try iter.seen_decls.putNoClobber(gpa, name, {});
4829 break :info .{
4830 name,
4831 .named,
4832 false,
4833 };
4834 },
4835 };
4836
4837 switch (kind) {
4838 .@"usingnamespace" => try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1),
4839 .@"test" => try zcu.test_functions.ensureUnusedCapacity(gpa, 1),
4840 else => {},
4841 }
4842
4843 const tracked_inst = try ip.trackZir(gpa, iter.parent_decl.getFileScope(zcu), decl_inst);
4844
4845 // We create a Decl for it regardless of analysis status.
4846
4847 const prev_exported, const decl_index = if (iter.existing_by_inst.get(tracked_inst)) |decl_index| decl_index: {
4848 // We need only update this existing Decl.
4849 const decl = zcu.declPtr(decl_index);
4850 const was_exported = decl.is_exported;
4851 assert(decl.kind == kind); // ZIR tracking should preserve this
4852 decl.name = decl_name;
4853 decl.src_line = line;
4854 decl.is_pub = declaration.flags.is_pub;
4855 decl.is_exported = declaration.flags.is_export;
4856 break :decl_index .{ was_exported, decl_index };
4857 } else decl_index: {
4858 // Create and set up a new Decl.
4859 const new_decl_index = try zcu.allocateNewDecl(namespace_index);
4860 const new_decl = zcu.declPtr(new_decl_index);
4861 new_decl.kind = kind;
4862 new_decl.name = decl_name;
4863 new_decl.src_line = line;
4864 new_decl.is_pub = declaration.flags.is_pub;
4865 new_decl.is_exported = declaration.flags.is_export;
4866 new_decl.zir_decl_index = tracked_inst.toOptional();
4867 break :decl_index .{ false, new_decl_index };
4868 };
4869
4870 const decl = zcu.declPtr(decl_index);
4871
4872 namespace.decls.putAssumeCapacityNoClobberContext(decl_index, {}, .{ .zcu = zcu });
4873
4874 const comp = zcu.comp;
4875 const decl_mod = namespace.file_scope.mod;
4876 const want_analysis = declaration.flags.is_export or switch (kind) {
4877 .anon => unreachable,
4878 .@"comptime" => true,
4879 .@"usingnamespace" => a: {
4880 namespace.usingnamespace_set.putAssumeCapacityNoClobber(decl_index, declaration.flags.is_pub);
4881 break :a true;
4882 },
4883 .named => false,
4884 .@"test" => a: {
4885 if (!comp.config.is_test) break :a false;
4886 if (decl_mod != zcu.main_mod) break :a false;
4887 if (is_named_test and comp.test_filters.len > 0) {
4888 const decl_fqn = try namespace.fullyQualifiedName(zcu, decl_name);
4889 const decl_fqn_slice = decl_fqn.toSlice(ip);
4890 for (comp.test_filters) |test_filter| {
4891 if (mem.indexOf(u8, decl_fqn_slice, test_filter)) |_| break;
4892 } else break :a false;
4893 }
4894 zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update
4895 break :a true;
4896 },
4897 };
4898
4899 if (want_analysis) {
4900 // We will not queue analysis if the decl has been analyzed on a previous update and
4901 // `is_export` is unchanged. In this case, the incremental update mechanism will handle
4902 // re-analysis for us if necessary.
4903 if (prev_exported != declaration.flags.is_export or decl.analysis == .unreferenced) {
4904 log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{}' decl_index={d}", .{
4905 namespace.file_scope.sub_file_path, decl_name.fmt(ip), decl_index,
4906 });
4907 comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = decl_index });
4908 }
4909 }
4910
4911 if (decl.getOwnedFunction(zcu) != null) {
4912 // TODO this logic is insufficient; namespaces we don't re-scan may still require
4913 // updated line numbers. Look into this!
4914 // TODO Look into detecting when this would be unnecessary by storing enough state
4915 // in `Decl` to notice that the line number did not change.
4916 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
4917 }
4918}
4919
4920/// Cancel the creation of an anon decl and delete any references to it.
4921/// If other decls depend on this decl, they must be aborted first.
4922pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
4923 assert(!mod.declIsRoot(decl_index));
4924 mod.destroyDecl(decl_index);
4925}
4926
4927/// Finalize the creation of an anon decl.
4928pub fn finalizeAnonDecl(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
4929 if (mod.declPtr(decl_index).typeOf(mod).isFnOrHasRuntimeBits(mod)) {
4930 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
4931 }
4932}
4933
4934/// Delete all the Export objects that are caused by this Decl. Re-analysis of
4935/// this Decl will cause them to be re-created (or not).
4936fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
4937 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
4938
4939 for (export_owners.items) |exp| {
4940 switch (exp.exported) {
4941 .decl_index => |exported_decl_index| {
4942 if (mod.decl_exports.getPtr(exported_decl_index)) |export_list| {
4943 // Remove exports with owner_decl matching the regenerating decl.
4944 const list = export_list.items;
4945 var i: usize = 0;
4946 var new_len = list.len;
4947 while (i < new_len) {
4948 if (list[i].owner_decl == decl_index) {
4949 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
4950 new_len -= 1;
4951 } else {
4952 i += 1;
4953 }
4954 }
4955 export_list.shrinkAndFree(mod.gpa, new_len);
4956 if (new_len == 0) {
4957 assert(mod.decl_exports.swapRemove(exported_decl_index));
4958 }
4959 }
4960 },
4961 .value => |value| {
4962 if (mod.value_exports.getPtr(value)) |export_list| {
4963 // Remove exports with owner_decl matching the regenerating decl.
4964 const list = export_list.items;
4965 var i: usize = 0;
4966 var new_len = list.len;
4967 while (i < new_len) {
4968 if (list[i].owner_decl == decl_index) {
4969 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
4970 new_len -= 1;
4971 } else {
4972 i += 1;
4973 }
4974 }
4975 export_list.shrinkAndFree(mod.gpa, new_len);
4976 if (new_len == 0) {
4977 assert(mod.value_exports.swapRemove(value));
4978 }
4979 }
4980 },
4981 }
4982 if (mod.comp.bin_file) |lf| {
4983 try lf.deleteDeclExport(decl_index, exp.opts.name);
4984 }
4985 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
4986 failed_kv.value.destroy(mod.gpa);
4987 }
4988 mod.gpa.destroy(exp);
4989 }
4990 export_owners.deinit(mod.gpa);
4991}
4992
4993pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocator) SemaError!Air {
4994 const tracy = trace(@src());
4995 defer tracy.end();
4996
4997 const gpa = mod.gpa;
4998 const ip = &mod.intern_pool;
4999 const func = mod.funcInfo(func_index);
5000 const decl_index = func.owner_decl;
5001 const decl = mod.declPtr(decl_index);
5002
5003 log.debug("func name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)});
5004 defer blk: {
5005 log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});
5006 }
5007
5008 const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
5009 defer decl_prog_node.end();
5010
5011 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index }));
5012
5013 var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
5014 defer comptime_err_ret_trace.deinit();
5015
5016 // In the case of a generic function instance, this is the type of the
5017 // instance, which has comptime parameters elided. In other words, it is
5018 // the runtime-known parameters only, not to be confused with the
5019 // generic_owner function type, which potentially has more parameters,
5020 // including comptime parameters.
5021 const fn_ty = decl.typeOf(mod);
5022 const fn_ty_info = mod.typeToFunc(fn_ty).?;
5023
5024 var sema: Sema = .{
5025 .mod = mod,
5026 .gpa = gpa,
5027 .arena = arena,
5028 .code = decl.getFileScope(mod).zir,
5029 .owner_decl = decl,
5030 .owner_decl_index = decl_index,
5031 .func_index = func_index,
5032 .func_is_naked = fn_ty_info.cc == .Naked,
5033 .fn_ret_ty = Type.fromInterned(fn_ty_info.return_type),
5034 .fn_ret_ty_ies = null,
5035 .owner_func_index = func_index,
5036 .branch_quota = @max(func.branchQuota(ip).*, Sema.default_branch_quota),
5037 .comptime_err_ret_trace = &comptime_err_ret_trace,
5038 };
5039 defer sema.deinit();
5040
5041 // Every runtime function has a dependency on the source of the Decl it originates from.
5042 // It also depends on the value of its owner Decl.
5043 try sema.declareDependency(.{ .src_hash = decl.zir_decl_index.unwrap().? });
5044 try sema.declareDependency(.{ .decl_val = decl_index });
5045
5046 if (func.analysis(ip).inferred_error_set) {
5047 const ies = try arena.create(Sema.InferredErrorSet);
5048 ies.* = .{ .func = func_index };
5049 sema.fn_ret_ty_ies = ies;
5050 }
5051
5052 // reset in case calls to errorable functions are removed.
5053 func.analysis(ip).calls_or_awaits_errorable_fn = false;
5054
5055 // First few indexes of extra are reserved and set at the end.
5056 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
5057 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
5058 sema.air_extra.items.len += reserved_count;
5059
5060 var inner_block: Sema.Block = .{
5061 .parent = null,
5062 .sema = &sema,
5063 .namespace = decl.src_namespace,
5064 .instructions = .{},
5065 .inlining = null,
5066 .is_comptime = false,
5067 .src_base_inst = inst: {
5068 const owner_info = if (func.generic_owner == .none)
5069 func
5070 else
5071 mod.funcInfo(func.generic_owner);
5072 const orig_decl = mod.declPtr(owner_info.owner_decl);
5073 break :inst orig_decl.zir_decl_index.unwrap().?;
5074 },
5075 .type_name_ctx = decl.name,
5076 };
5077 defer inner_block.instructions.deinit(gpa);
5078
5079 const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).resolve(ip));
5080
5081 // Here we are performing "runtime semantic analysis" for a function body, which means
5082 // we must map the parameter ZIR instructions to `arg` AIR instructions.
5083 // AIR requires the `arg` parameters to be the first N instructions.
5084 // This could be a generic function instantiation, however, in which case we need to
5085 // map the comptime parameters to constant values and only emit arg AIR instructions
5086 // for the runtime ones.
5087 const runtime_params_len = fn_ty_info.param_types.len;
5088 try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len);
5089 try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len);
5090 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
5091
5092 // In the case of a generic function instance, pre-populate all the comptime args.
5093 if (func.comptime_args.len != 0) {
5094 for (
5095 fn_info.param_body[0..func.comptime_args.len],
5096 func.comptime_args.get(ip),
5097 ) |inst, comptime_arg| {
5098 if (comptime_arg == .none) continue;
5099 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.internedToRef(comptime_arg));
5100 }
5101 }
5102
5103 const src_params_len = if (func.comptime_args.len != 0)
5104 func.comptime_args.len
5105 else
5106 runtime_params_len;
5107
5108 var runtime_param_index: usize = 0;
5109 for (fn_info.param_body[0..src_params_len], 0..) |inst, src_param_index| {
5110 const gop = sema.inst_map.getOrPutAssumeCapacity(inst);
5111 if (gop.found_existing) continue; // provided above by comptime arg
5112
5113 const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
5114 runtime_param_index += 1;
5115
5116 const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) {
5117 error.GenericPoison => unreachable,
5118 error.ComptimeReturn => unreachable,
5119 error.ComptimeBreak => unreachable,
5120 else => |e| return e,
5121 };
5122 if (opt_opv) |opv| {
5123 gop.value_ptr.* = Air.internedToRef(opv.toIntern());
5124 continue;
5125 }
5126 const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
5127 gop.value_ptr.* = arg_index.toRef();
5128 inner_block.instructions.appendAssumeCapacity(arg_index);
5129 sema.air_instructions.appendAssumeCapacity(.{
5130 .tag = .arg,
5131 .data = .{ .arg = .{
5132 .ty = Air.internedToRef(param_ty),
5133 .src_index = @intCast(src_param_index),
5134 } },
5135 });
5136 }
5137
5138 func.analysis(ip).state = .in_progress;
5139
5140 const last_arg_index = inner_block.instructions.items.len;
5141
5142 // Save the error trace as our first action in the function.
5143 // If this is unnecessary after all, Liveness will clean it up for us.
5144 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&inner_block);
5145 sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
5146 inner_block.error_return_trace_index = error_return_trace_index;
5147
5148 sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) {
5149 // TODO make these unreachable instead of @panic
5150 error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
5151 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
5152 else => |e| return e,
5153 };
5154
5155 for (sema.unresolved_inferred_allocs.keys()) |ptr_inst| {
5156 // The lack of a resolve_inferred_alloc means that this instruction
5157 // is unused so it just has to be a no-op.
5158 sema.air_instructions.set(@intFromEnum(ptr_inst), .{
5159 .tag = .alloc,
5160 .data = .{ .ty = Type.single_const_pointer_to_comptime_int },
5161 });
5162 }
5163
5164 // If we don't get an error return trace from a caller, create our own.
5165 if (func.analysis(ip).calls_or_awaits_errorable_fn and
5166 mod.comp.config.any_error_tracing and
5167 !sema.fn_ret_ty.isError(mod))
5168 {
5169 sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) {
5170 // TODO make these unreachable instead of @panic
5171 error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
5172 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
5173 error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"),
5174 else => |e| return e,
5175 };
5176 }
5177
5178 // Copy the block into place and mark that as the main block.
5179 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
5180 inner_block.instructions.items.len);
5181 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
5182 .body_len = @intCast(inner_block.instructions.items.len),
5183 });
5184 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(inner_block.instructions.items));
5185 sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;
5186
5187 // Resolving inferred error sets is done *before* setting the function
5188 // state to success, so that "unable to resolve inferred error set" errors
5189 // can be emitted here.
5190 if (sema.fn_ret_ty_ies) |ies| {
5191 sema.resolveInferredErrorSetPtr(&inner_block, .{
5192 .base_node_inst = inner_block.src_base_inst,
5193 .offset = LazySrcLoc.Offset.nodeOffset(0),
5194 }, ies) catch |err| switch (err) {
5195 error.GenericPoison => unreachable,
5196 error.ComptimeReturn => unreachable,
5197 error.ComptimeBreak => unreachable,
5198 error.AnalysisFail => {
5199 // In this case our function depends on a type that had a compile error.
5200 // We should not try to lower this function.
5201 decl.analysis = .dependency_failure;
5202 return error.AnalysisFail;
5203 },
5204 else => |e| return e,
5205 };
5206 assert(ies.resolved != .none);
5207 ip.funcIesResolved(func_index).* = ies.resolved;
5208 }
5209
5210 func.analysis(ip).state = .success;
5211
5212 // Finally we must resolve the return type and parameter types so that backends
5213 // have full access to type information.
5214 // Crucially, this happens *after* we set the function state to success above,
5215 // so that dependencies on the function body will now be satisfied rather than
5216 // result in circular dependency errors.
5217 sema.resolveFnTypes(fn_ty) catch |err| switch (err) {
5218 error.GenericPoison => unreachable,
5219 error.ComptimeReturn => unreachable,
5220 error.ComptimeBreak => unreachable,
5221 error.AnalysisFail => {
5222 // In this case our function depends on a type that had a compile error.
5223 // We should not try to lower this function.
5224 decl.analysis = .dependency_failure;
5225 return error.AnalysisFail;
5226 },
5227 else => |e| return e,
5228 };
5229
5230 // Similarly, resolve any queued up types that were requested to be resolved for
5231 // the backends.
5232 for (sema.types_to_resolve.keys()) |ty| {
5233 sema.resolveTypeFully(Type.fromInterned(ty)) catch |err| switch (err) {
5234 error.GenericPoison => unreachable,
5235 error.ComptimeReturn => unreachable,
5236 error.ComptimeBreak => unreachable,
5237 error.AnalysisFail => {
5238 // In this case our function depends on a type that had a compile error.
5239 // We should not try to lower this function.
5240 decl.analysis = .dependency_failure;
5241 return error.AnalysisFail;
5242 },
5243 else => |e| return e,
5244 };
5245 }
5246
5247 return .{
5248 .instructions = sema.air_instructions.toOwnedSlice(),
5249 .extra = try sema.air_extra.toOwnedSlice(gpa),
5250 };
5251}
5252
5253pub fn createNamespace(mod: *Module, initialization: Namespace) !Namespace.Index {
5254 return mod.intern_pool.createNamespace(mod.gpa, initialization);
5255}
5256
5257pub fn destroyNamespace(mod: *Module, index: Namespace.Index) void {
5258 return mod.intern_pool.destroyNamespace(mod.gpa, index);
5259}
5260
5261pub fn allocateNewDecl(zcu: *Zcu, namespace: Namespace.Index) !Decl.Index {
5262 const gpa = zcu.gpa;
5263 const decl_index = try zcu.intern_pool.createDecl(gpa, .{
5264 .name = undefined,
5265 .src_namespace = namespace,
5266 .src_line = undefined,
5267 .has_tv = false,
5268 .owns_tv = false,
5269 .val = undefined,
5270 .alignment = undefined,
5271 .@"linksection" = .none,
5272 .@"addrspace" = .generic,
5273 .analysis = .unreferenced,
5274 .zir_decl_index = .none,
5275 .is_pub = false,
5276 .is_exported = false,
5277 .kind = .anon,
5278 });
5279
5280 if (zcu.emit_h) |zcu_emit_h| {
5281 if (@intFromEnum(decl_index) >= zcu_emit_h.allocated_emit_h.len) {
5282 try zcu_emit_h.allocated_emit_h.append(gpa, .{});
5283 assert(@intFromEnum(decl_index) == zcu_emit_h.allocated_emit_h.len);
5284 }
5285 }
5286
5287 return decl_index;
5288}
5289
5290pub fn getErrorValue(
5291 mod: *Module,
5292 name: InternPool.NullTerminatedString,
5293) Allocator.Error!ErrorInt {
5294 const gop = try mod.global_error_set.getOrPut(mod.gpa, name);
5295 return @as(ErrorInt, @intCast(gop.index));
5296}
5297
5298pub fn getErrorValueFromSlice(
5299 mod: *Module,
5300 name: []const u8,
5301) Allocator.Error!ErrorInt {
5302 const interned_name = try mod.intern_pool.getOrPutString(mod.gpa, name);
5303 return getErrorValue(mod, interned_name);
5304}
5305
5306pub fn errorSetBits(mod: *Module) u16 {
5307 if (mod.error_limit == 0) return 0;
5308 return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error
5309}
5310
5311pub fn initNewAnonDecl(
5312 mod: *Module,
5313 new_decl_index: Decl.Index,
5314 src_line: u32,
5315 val: Value,
5316 name: InternPool.NullTerminatedString,
5317) Allocator.Error!void {
5318 const new_decl = mod.declPtr(new_decl_index);
5319
5320 new_decl.name = name;
5321 new_decl.src_line = src_line;
5322 new_decl.val = val;
5323 new_decl.alignment = .none;
5324 new_decl.@"linksection" = .none;
5325 new_decl.has_tv = true;
5326 new_decl.analysis = .complete;
5327}
5328
5329pub fn errNoteNonLazy(
5330 mod: *Module,
5331 src_loc: SrcLoc,
5332 parent: *ErrorMsg,
5333 comptime format: []const u8,
5334 args: anytype,
5335) error{OutOfMemory}!void {
5336 if (src_loc.lazy == .unneeded) {
5337 assert(parent.src_loc.lazy == .unneeded);
5338 return;
5339 }
5340 const msg = try std.fmt.allocPrint(mod.gpa, format, args);
5341 errdefer mod.gpa.free(msg);
5342
5343 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
5344 parent.notes[parent.notes.len - 1] = .{
5345 .src_loc = src_loc,
5346 .msg = msg,
5347 };
5348}
5349
5350/// Deprecated. There is no global target for a Zig Compilation Unit. Instead,
5351/// look up the target based on the Module that contains the source code being
5352/// analyzed.
5353pub fn getTarget(zcu: Module) Target {
5354 return zcu.root_mod.resolved_target.result;
5355}
5356
5357/// Deprecated. There is no global optimization mode for a Zig Compilation
5358/// Unit. Instead, look up the optimization mode based on the Module that
5359/// contains the source code being analyzed.
5360pub fn optimizeMode(zcu: Module) std.builtin.OptimizeMode {
5361 return zcu.root_mod.optimize_mode;
5362}
5363
5364fn lockAndClearFileCompileError(mod: *Module, file: *File) void {
5365 switch (file.status) {
5366 .success_zir, .retryable_failure => {},
5367 .never_loaded, .parse_failure, .astgen_failure => {
5368 mod.comp.mutex.lock();
5369 defer mod.comp.mutex.unlock();
5370 if (mod.failed_files.fetchSwapRemove(file)) |kv| {
5371 if (kv.value) |msg| msg.destroy(mod.gpa); // Delete previous error message.
5372 }
5373 },
5374 }
5375}
5376
5377/// Called from `Compilation.update`, after everything is done, just before
5378/// reporting compile errors. In this function we emit exported symbol collision
5379/// errors and communicate exported symbols to the linker backend.
5380pub fn processExports(mod: *Module) !void {
5381 // Map symbol names to `Export` for name collision detection.
5382 var symbol_exports: SymbolExports = .{};
5383 defer symbol_exports.deinit(mod.gpa);
5384
5385 for (mod.decl_exports.keys(), mod.decl_exports.values()) |exported_decl, exports_list| {
5386 const exported: Exported = .{ .decl_index = exported_decl };
5387 try processExportsInner(mod, &symbol_exports, exported, exports_list.items);
5388 }
5389
5390 for (mod.value_exports.keys(), mod.value_exports.values()) |exported_value, exports_list| {
5391 const exported: Exported = .{ .value = exported_value };
5392 try processExportsInner(mod, &symbol_exports, exported, exports_list.items);
5393 }
5394}
5395
5396const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export);
5397
5398fn processExportsInner(
5399 zcu: *Zcu,
5400 symbol_exports: *SymbolExports,
5401 exported: Exported,
5402 exports: []const *Export,
5403) error{OutOfMemory}!void {
5404 const gpa = zcu.gpa;
5405
5406 for (exports) |new_export| {
5407 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
5408 if (gop.found_existing) {
5409 new_export.status = .failed_retryable;
5410 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
5411 const src_loc = new_export.getSrcLoc(zcu);
5412 const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{
5413 new_export.opts.name.fmt(&zcu.intern_pool),
5414 });
5415 errdefer msg.destroy(gpa);
5416 const other_export = gop.value_ptr.*;
5417 const other_src_loc = other_export.getSrcLoc(zcu);
5418 try zcu.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{});
5419 zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5420 new_export.status = .failed;
5421 } else {
5422 gop.value_ptr.* = new_export;
5423 }
5424 }
5425 if (zcu.comp.bin_file) |lf| {
5426 try handleUpdateExports(zcu, exports, lf.updateExports(zcu, exported, exports));
5427 } else if (zcu.llvm_object) |llvm_object| {
5428 if (build_options.only_c) unreachable;
5429 try handleUpdateExports(zcu, exports, llvm_object.updateExports(zcu, exported, exports));
5430 }
5431}
5432
5433fn handleUpdateExports(
5434 zcu: *Zcu,
5435 exports: []const *Export,
5436 result: link.File.UpdateExportsError!void,
5437) Allocator.Error!void {
5438 const gpa = zcu.gpa;
5439 result catch |err| switch (err) {
5440 error.OutOfMemory => return error.OutOfMemory,
5441 error.AnalysisFail => {
5442 const new_export = exports[0];
5443 new_export.status = .failed_retryable;
5444 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
5445 const src_loc = new_export.getSrcLoc(zcu);
5446 const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{
5447 @errorName(err),
5448 });
5449 zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5450 },
5451 };
5452}
5453
5454pub fn populateTestFunctions(
5455 mod: *Module,
5456 main_progress_node: std.Progress.Node,
5457) !void {
5458 const gpa = mod.gpa;
5459 const ip = &mod.intern_pool;
5460 const builtin_mod = mod.root_mod.getBuiltinDependency();
5461 const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file;
5462 const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);
5463 const builtin_namespace = mod.namespacePtr(root_decl.src_namespace);
5464 const test_functions_str = try ip.getOrPutString(gpa, "test_functions", .no_embedded_nulls);
5465 const decl_index = builtin_namespace.decls.getKeyAdapted(
5466 test_functions_str,
5467 DeclAdapter{ .zcu = mod },
5468 ).?;
5469 {
5470 // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions`
5471 // was not referenced by start code.
5472 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
5473 defer {
5474 mod.sema_prog_node.end();
5475 mod.sema_prog_node = undefined;
5476 }
5477 try mod.ensureDeclAnalyzed(decl_index);
5478 }
5479
5480 const decl = mod.declPtr(decl_index);
5481 const test_fn_ty = decl.typeOf(mod).slicePtrFieldType(mod).childType(mod);
5482
5483 const array_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = array: {
5484 // Add mod.test_functions to an array decl then make the test_functions
5485 // decl reference it as a slice.
5486 const test_fn_vals = try gpa.alloc(InternPool.Index, mod.test_functions.count());
5487 defer gpa.free(test_fn_vals);
5488
5489 for (test_fn_vals, mod.test_functions.keys()) |*test_fn_val, test_decl_index| {
5490 const test_decl = mod.declPtr(test_decl_index);
5491 const test_decl_name = try test_decl.fullyQualifiedName(mod);
5492 const test_decl_name_len = test_decl_name.length(ip);
5493 const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = n: {
5494 const test_name_ty = try mod.arrayType(.{
5495 .len = test_decl_name_len,
5496 .child = .u8_type,
5497 });
5498 const test_name_val = try mod.intern(.{ .aggregate = .{
5499 .ty = test_name_ty.toIntern(),
5500 .storage = .{ .bytes = test_decl_name.toString() },
5501 } });
5502 break :n .{
5503 .orig_ty = (try mod.singleConstPtrType(test_name_ty)).toIntern(),
5504 .val = test_name_val,
5505 };
5506 };
5507
5508 const test_fn_fields = .{
5509 // name
5510 try mod.intern(.{ .slice = .{
5511 .ty = .slice_const_u8_type,
5512 .ptr = try mod.intern(.{ .ptr = .{
5513 .ty = .manyptr_const_u8_type,
5514 .base_addr = .{ .anon_decl = test_name_anon_decl },
5515 .byte_offset = 0,
5516 } }),
5517 .len = try mod.intern(.{ .int = .{
5518 .ty = .usize_type,
5519 .storage = .{ .u64 = test_decl_name_len },
5520 } }),
5521 } }),
5522 // func
5523 try mod.intern(.{ .ptr = .{
5524 .ty = try mod.intern(.{ .ptr_type = .{
5525 .child = test_decl.typeOf(mod).toIntern(),
5526 .flags = .{
5527 .is_const = true,
5528 },
5529 } }),
5530 .base_addr = .{ .decl = test_decl_index },
5531 .byte_offset = 0,
5532 } }),
5533 };
5534 test_fn_val.* = try mod.intern(.{ .aggregate = .{
5535 .ty = test_fn_ty.toIntern(),
5536 .storage = .{ .elems = &test_fn_fields },
5537 } });
5538 }
5539
5540 const array_ty = try mod.arrayType(.{
5541 .len = test_fn_vals.len,
5542 .child = test_fn_ty.toIntern(),
5543 .sentinel = .none,
5544 });
5545 const array_val = try mod.intern(.{ .aggregate = .{
5546 .ty = array_ty.toIntern(),
5547 .storage = .{ .elems = test_fn_vals },
5548 } });
5549 break :array .{
5550 .orig_ty = (try mod.singleConstPtrType(array_ty)).toIntern(),
5551 .val = array_val,
5552 };
5553 };
5554
5555 {
5556 const new_ty = try mod.ptrType(.{
5557 .child = test_fn_ty.toIntern(),
5558 .flags = .{
5559 .is_const = true,
5560 .size = .Slice,
5561 },
5562 });
5563 const new_val = decl.val;
5564 const new_init = try mod.intern(.{ .slice = .{
5565 .ty = new_ty.toIntern(),
5566 .ptr = try mod.intern(.{ .ptr = .{
5567 .ty = new_ty.slicePtrFieldType(mod).toIntern(),
5568 .base_addr = .{ .anon_decl = array_anon_decl },
5569 .byte_offset = 0,
5570 } }),
5571 .len = (try mod.intValue(Type.usize, mod.test_functions.count())).toIntern(),
5572 } });
5573 ip.mutateVarInit(decl.val.toIntern(), new_init);
5574
5575 // Since we are replacing the Decl's value we must perform cleanup on the
5576 // previous value.
5577 decl.val = new_val;
5578 decl.has_tv = true;
5579 }
5580 {
5581 mod.codegen_prog_node = main_progress_node.start("Code Generation", 0);
5582 defer {
5583 mod.codegen_prog_node.end();
5584 mod.codegen_prog_node = undefined;
5585 }
5586
5587 try mod.linkerUpdateDecl(decl_index);
5588 }
5589}
5590
5591pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {
5592 const comp = zcu.comp;
5593
5594 const decl = zcu.declPtr(decl_index);
5595
5596 const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(&zcu.intern_pool), 0);
5597 defer codegen_prog_node.end();
5598
5599 if (comp.bin_file) |lf| {
5600 lf.updateDecl(zcu, decl_index) catch |err| switch (err) {
5601 error.OutOfMemory => return error.OutOfMemory,
5602 error.AnalysisFail => {
5603 decl.analysis = .codegen_failure;
5604 },
5605 else => {
5606 const gpa = zcu.gpa;
5607 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1);
5608 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
5609 gpa,
5610 decl.navSrcLoc(zcu).upgrade(zcu),
5611 "unable to codegen: {s}",
5612 .{@errorName(err)},
5613 ));
5614 decl.analysis = .codegen_failure;
5615 try zcu.retryable_failures.append(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index }));
5616 },
5617 };
5618 } else if (zcu.llvm_object) |llvm_object| {
5619 if (build_options.only_c) unreachable;
5620 llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) {
5621 error.OutOfMemory => return error.OutOfMemory,
5622 error.AnalysisFail => {
5623 decl.analysis = .codegen_failure;
5624 },
5625 };
5626 }
5627}
5628
5629fn reportRetryableFileError(
5630 mod: *Module,
5631 file: *File,
5632 comptime format: []const u8,
5633 args: anytype,
5634) error{OutOfMemory}!void {
5635 file.status = .retryable_failure;
5636
5637 const err_msg = try ErrorMsg.create(
5638 mod.gpa,
5639 .{
5640 .file_scope = file,
5641 .base_node = 0,
5642 .lazy = .entire_file,
5643 },
5644 format,
5645 args,
5646 );
5647 errdefer err_msg.destroy(mod.gpa);
5648
5649 mod.comp.mutex.lock();
5650 defer mod.comp.mutex.unlock();
5651
5652 const gop = try mod.failed_files.getOrPut(mod.gpa, file);
5653 if (gop.found_existing) {
5654 if (gop.value_ptr.*) |old_err_msg| {
5655 old_err_msg.destroy(mod.gpa);
5656 }
5657 }
5658 gop.value_ptr.* = err_msg;
5659}
5660
5661pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void {
5662 const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index);
5663 if (gop.found_existing) {
5664 const new_value = try std.fmt.allocPrint(mod.gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
5665 mod.gpa.free(gop.value_ptr.*);
5666 gop.value_ptr.* = new_value;
5667 } else {
5668 gop.value_ptr.* = try mod.gpa.dupe(u8, source);
5669 }
5670}
5671
5672pub fn getDeclExports(mod: Module, decl_index: Decl.Index) []const *Export {
5673 if (mod.decl_exports.get(decl_index)) |l| {
5674 return l.items;
5675 } else {
5676 return &[0]*Export{};
5677 }
5678}
5679
5680pub const Feature = enum {
5681 panic_fn,
5682 panic_unwrap_error,
5683 safety_check_formatted,
5684 error_return_trace,
5685 is_named_enum_value,
5686 error_set_has_value,
5687 field_reordering,
5688 /// When this feature is supported, the backend supports the following AIR instructions:
5689 /// * `Air.Inst.Tag.add_safe`
5690 /// * `Air.Inst.Tag.sub_safe`
5691 /// * `Air.Inst.Tag.mul_safe`
5692 /// The motivation for this feature is that it makes AIR smaller, and makes it easier
5693 /// to generate better machine code in the backends. All backends should migrate to
5694 /// enabling this feature.
5695 safety_checked_instructions,
5696};
5697
5698pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool {
5699 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch;
5700 const ofmt = zcu.root_mod.resolved_target.result.ofmt;
5701 const use_llvm = zcu.comp.config.use_llvm;
5702 return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature);
5703}
5704
5705/// Shortcut for calling `intern_pool.get`.
5706pub fn intern(mod: *Module, key: InternPool.Key) Allocator.Error!InternPool.Index {
5707 return mod.intern_pool.get(mod.gpa, key);
5708}
5709
5710/// Shortcut for calling `intern_pool.getCoerced`.
5711pub fn getCoerced(mod: *Module, val: Value, new_ty: Type) Allocator.Error!Value {
5712 return Value.fromInterned((try mod.intern_pool.getCoerced(mod.gpa, val.toIntern(), new_ty.toIntern())));
5713}
5714
5715pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type {
5716 return Type.fromInterned((try intern(mod, .{ .int_type = .{
5717 .signedness = signedness,
5718 .bits = bits,
5719 } })));
5720}
5721
5722pub fn errorIntType(mod: *Module) std.mem.Allocator.Error!Type {
5723 return mod.intType(.unsigned, mod.errorSetBits());
5724}
5725
5726pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type {
5727 const i = try intern(mod, .{ .array_type = info });
5728 return Type.fromInterned(i);
5729}
5730
5731pub fn vectorType(mod: *Module, info: InternPool.Key.VectorType) Allocator.Error!Type {
5732 const i = try intern(mod, .{ .vector_type = info });
5733 return Type.fromInterned(i);
5734}
5735
5736pub fn optionalType(mod: *Module, child_type: InternPool.Index) Allocator.Error!Type {
5737 const i = try intern(mod, .{ .opt_type = child_type });
5738 return Type.fromInterned(i);
5739}
5740
5741pub fn ptrType(mod: *Module, info: InternPool.Key.PtrType) Allocator.Error!Type {
5742 var canon_info = info;
5743
5744 if (info.flags.size == .C) canon_info.flags.is_allowzero = true;
5745
5746 // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee
5747 // type, we change it to 0 here. If this causes an assertion trip because the
5748 // pointee type needs to be resolved more, that needs to be done before calling
5749 // this ptr() function.
5750 if (info.flags.alignment != .none and
5751 info.flags.alignment == Type.fromInterned(info.child).abiAlignment(mod))
5752 {
5753 canon_info.flags.alignment = .none;
5754 }
5755
5756 switch (info.flags.vector_index) {
5757 // Canonicalize host_size. If it matches the bit size of the pointee type,
5758 // we change it to 0 here. If this causes an assertion trip, the pointee type
5759 // needs to be resolved before calling this ptr() function.
5760 .none => if (info.packed_offset.host_size != 0) {
5761 const elem_bit_size = Type.fromInterned(info.child).bitSize(mod);
5762 assert(info.packed_offset.bit_offset + elem_bit_size <= info.packed_offset.host_size * 8);
5763 if (info.packed_offset.host_size * 8 == elem_bit_size) {
5764 canon_info.packed_offset.host_size = 0;
5765 }
5766 },
5767 .runtime => {},
5768 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
5769 }
5770
5771 return Type.fromInterned((try intern(mod, .{ .ptr_type = canon_info })));
5772}
5773
5774pub fn singleMutPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
5775 return ptrType(mod, .{ .child = child_type.toIntern() });
5776}
5777
5778pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
5779 return ptrType(mod, .{
5780 .child = child_type.toIntern(),
5781 .flags = .{
5782 .is_const = true,
5783 },
5784 });
5785}
5786
5787pub fn manyConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
5788 return ptrType(mod, .{
5789 .child = child_type.toIntern(),
5790 .flags = .{
5791 .size = .Many,
5792 .is_const = true,
5793 },
5794 });
5795}
5796
5797pub fn adjustPtrTypeChild(mod: *Module, ptr_ty: Type, new_child: Type) Allocator.Error!Type {
5798 var info = ptr_ty.ptrInfo(mod);
5799 info.child = new_child.toIntern();
5800 return mod.ptrType(info);
5801}
5802
5803pub fn funcType(mod: *Module, key: InternPool.GetFuncTypeKey) Allocator.Error!Type {
5804 return Type.fromInterned((try mod.intern_pool.getFuncType(mod.gpa, key)));
5805}
5806
5807/// Use this for `anyframe->T` only.
5808/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
5809pub fn anyframeType(mod: *Module, payload_ty: Type) Allocator.Error!Type {
5810 return Type.fromInterned((try intern(mod, .{ .anyframe_type = payload_ty.toIntern() })));
5811}
5812
5813pub fn errorUnionType(mod: *Module, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {
5814 return Type.fromInterned((try intern(mod, .{ .error_union_type = .{
5815 .error_set_type = error_set_ty.toIntern(),
5816 .payload_type = payload_ty.toIntern(),
5817 } })));
5818}
5819
5820pub fn singleErrorSetType(mod: *Module, name: InternPool.NullTerminatedString) Allocator.Error!Type {
5821 const names: *const [1]InternPool.NullTerminatedString = &name;
5822 const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names);
5823 return Type.fromInterned(new_ty);
5824}
5825
5826/// Sorts `names` in place.
5827pub fn errorSetFromUnsortedNames(
5828 mod: *Module,
5829 names: []InternPool.NullTerminatedString,
5830) Allocator.Error!Type {
5831 std.mem.sort(
5832 InternPool.NullTerminatedString,
5833 names,
5834 {},
5835 InternPool.NullTerminatedString.indexLessThan,
5836 );
5837 const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names);
5838 return Type.fromInterned(new_ty);
5839}
5840
5841/// Supports only pointers, not pointer-like optionals.
5842pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
5843 assert(ty.zigTypeTag(mod) == .Pointer and !ty.isSlice(mod));
5844 assert(x != 0 or ty.isAllowzeroPtr(mod));
5845 const i = try intern(mod, .{ .ptr = .{
5846 .ty = ty.toIntern(),
5847 .base_addr = .int,
5848 .byte_offset = x,
5849 } });
5850 return Value.fromInterned(i);
5851}
5852
5853/// Creates an enum tag value based on the integer tag value.
5854pub fn enumValue(mod: *Module, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value {
5855 if (std.debug.runtime_safety) {
5856 const tag = ty.zigTypeTag(mod);
5857 assert(tag == .Enum);
5858 }
5859 const i = try intern(mod, .{ .enum_tag = .{
5860 .ty = ty.toIntern(),
5861 .int = tag_int,
5862 } });
5863 return Value.fromInterned(i);
5864}
5865
5866/// Creates an enum tag value based on the field index according to source code
5867/// declaration order.
5868pub fn enumValueFieldIndex(mod: *Module, ty: Type, field_index: u32) Allocator.Error!Value {
5869 const ip = &mod.intern_pool;
5870 const gpa = mod.gpa;
5871 const enum_type = ip.loadEnumType(ty.toIntern());
5872
5873 if (enum_type.values.len == 0) {
5874 // Auto-numbered fields.
5875 return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{
5876 .ty = ty.toIntern(),
5877 .int = try ip.get(gpa, .{ .int = .{
5878 .ty = enum_type.tag_ty,
5879 .storage = .{ .u64 = field_index },
5880 } }),
5881 } })));
5882 }
5883
5884 return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{
5885 .ty = ty.toIntern(),
5886 .int = enum_type.values.get(ip)[field_index],
5887 } })));
5888}
5889
5890pub fn undefValue(mod: *Module, ty: Type) Allocator.Error!Value {
5891 return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
5892}
5893
5894pub fn undefRef(mod: *Module, ty: Type) Allocator.Error!Air.Inst.Ref {
5895 return Air.internedToRef((try mod.undefValue(ty)).toIntern());
5896}
5897
5898pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
5899 if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
5900 if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
5901 var limbs_buffer: [4]usize = undefined;
5902 var big_int = BigIntMutable.init(&limbs_buffer, x);
5903 return intValue_big(mod, ty, big_int.toConst());
5904}
5905
5906pub fn intRef(mod: *Module, ty: Type, x: anytype) Allocator.Error!Air.Inst.Ref {
5907 return Air.internedToRef((try mod.intValue(ty, x)).toIntern());
5908}
5909
5910pub fn intValue_big(mod: *Module, ty: Type, x: BigIntConst) Allocator.Error!Value {
5911 const i = try intern(mod, .{ .int = .{
5912 .ty = ty.toIntern(),
5913 .storage = .{ .big_int = x },
5914 } });
5915 return Value.fromInterned(i);
5916}
5917
5918pub fn intValue_u64(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
5919 const i = try intern(mod, .{ .int = .{
5920 .ty = ty.toIntern(),
5921 .storage = .{ .u64 = x },
5922 } });
5923 return Value.fromInterned(i);
5924}
5925
5926pub fn intValue_i64(mod: *Module, ty: Type, x: i64) Allocator.Error!Value {
5927 const i = try intern(mod, .{ .int = .{
5928 .ty = ty.toIntern(),
5929 .storage = .{ .i64 = x },
5930 } });
5931 return Value.fromInterned(i);
5932}
5933
5934pub fn unionValue(mod: *Module, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value {
5935 const i = try intern(mod, .{ .un = .{
5936 .ty = union_ty.toIntern(),
5937 .tag = tag.toIntern(),
5938 .val = val.toIntern(),
5939 } });
5940 return Value.fromInterned(i);
5941}
5942
5943/// This function casts the float representation down to the representation of the type, potentially
5944/// losing data if the representation wasn't correct.
5945pub fn floatValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
5946 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(mod.getTarget())) {
5947 16 => .{ .f16 = @as(f16, @floatCast(x)) },
5948 32 => .{ .f32 = @as(f32, @floatCast(x)) },
5949 64 => .{ .f64 = @as(f64, @floatCast(x)) },
5950 80 => .{ .f80 = @as(f80, @floatCast(x)) },
5951 128 => .{ .f128 = @as(f128, @floatCast(x)) },
5952 else => unreachable,
5953 };
5954 const i = try intern(mod, .{ .float = .{
5955 .ty = ty.toIntern(),
5956 .storage = storage,
5957 } });
5958 return Value.fromInterned(i);
5959}
5960
5961pub fn nullValue(mod: *Module, opt_ty: Type) Allocator.Error!Value {
5962 const ip = &mod.intern_pool;
5963 assert(ip.isOptionalType(opt_ty.toIntern()));
5964 const result = try ip.get(mod.gpa, .{ .opt = .{
5965 .ty = opt_ty.toIntern(),
5966 .val = .none,
5967 } });
5968 return Value.fromInterned(result);
5969}
5970
5971pub fn smallestUnsignedInt(mod: *Module, max: u64) Allocator.Error!Type {
5972 return intType(mod, .unsigned, Type.smallestUnsignedBits(max));
5973}
5974
5975/// Returns the smallest possible integer type containing both `min` and
5976/// `max`. Asserts that neither value is undef.
5977/// TODO: if #3806 is implemented, this becomes trivial
5978pub fn intFittingRange(mod: *Module, min: Value, max: Value) !Type {
5979 assert(!min.isUndef(mod));
5980 assert(!max.isUndef(mod));
5981
5982 if (std.debug.runtime_safety) {
5983 assert(Value.order(min, max, mod).compare(.lte));
5984 }
5985
5986 const sign = min.orderAgainstZero(mod) == .lt;
5987
5988 const min_val_bits = intBitsForValue(mod, min, sign);
5989 const max_val_bits = intBitsForValue(mod, max, sign);
5990
5991 return mod.intType(
5992 if (sign) .signed else .unsigned,
5993 @max(min_val_bits, max_val_bits),
5994 );
5995}
5996
5997/// Given a value representing an integer, returns the number of bits necessary to represent
5998/// this value in an integer. If `sign` is true, returns the number of bits necessary in a
5999/// twos-complement integer; otherwise in an unsigned integer.
6000/// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true.
6001pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
6002 assert(!val.isUndef(mod));
6003
6004 const key = mod.intern_pool.indexToKey(val.toIntern());
6005 switch (key.int.storage) {
6006 .i64 => |x| {
6007 if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted) + @intFromBool(sign);
6008 assert(sign);
6009 // Protect against overflow in the following negation.
6010 if (x == std.math.minInt(i64)) return 64;
6011 return Type.smallestUnsignedBits(@as(u64, @intCast(-(x + 1)))) + 1;
6012 },
6013 .u64 => |x| {
6014 return Type.smallestUnsignedBits(x) + @intFromBool(sign);
6015 },
6016 .big_int => |big| {
6017 if (big.positive) return @as(u16, @intCast(big.bitCountAbs() + @intFromBool(sign)));
6018
6019 // Zero is still a possibility, in which case unsigned is fine
6020 if (big.eqlZero()) return 0;
6021
6022 return @as(u16, @intCast(big.bitCountTwosComp()));
6023 },
6024 .lazy_align => |lazy_ty| {
6025 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(mod).toByteUnits() orelse 0) + @intFromBool(sign);
6026 },
6027 .lazy_size => |lazy_ty| {
6028 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(mod)) + @intFromBool(sign);
6029 },
6030 }
6031}
6032
6033pub const AtomicPtrAlignmentError = error{
6034 FloatTooBig,
6035 IntTooBig,
6036 BadType,
6037 OutOfMemory,
6038};
6039
6040pub const AtomicPtrAlignmentDiagnostics = struct {
6041 bits: u16 = undefined,
6042 max_bits: u16 = undefined,
6043};
6044
6045/// If ABI alignment of `ty` is OK for atomic operations, returns 0.
6046/// Otherwise returns the alignment required on a pointer for the target
6047/// to perform atomic operations.
6048// TODO this function does not take into account CPU features, which can affect
6049// this value. Audit this!
6050pub fn atomicPtrAlignment(
6051 mod: *Module,
6052 ty: Type,
6053 diags: *AtomicPtrAlignmentDiagnostics,
6054) AtomicPtrAlignmentError!Alignment {
6055 const target = mod.getTarget();
6056 const max_atomic_bits: u16 = switch (target.cpu.arch) {
6057 .avr,
6058 .msp430,
6059 .spu_2,
6060 => 16,
6061
6062 .arc,
6063 .arm,
6064 .armeb,
6065 .hexagon,
6066 .m68k,
6067 .le32,
6068 .mips,
6069 .mipsel,
6070 .nvptx,
6071 .powerpc,
6072 .powerpcle,
6073 .r600,
6074 .riscv32,
6075 .sparc,
6076 .sparcel,
6077 .tce,
6078 .tcele,
6079 .thumb,
6080 .thumbeb,
6081 .x86,
6082 .xcore,
6083 .amdil,
6084 .hsail,
6085 .spir,
6086 .kalimba,
6087 .lanai,
6088 .shave,
6089 .wasm32,
6090 .renderscript32,
6091 .csky,
6092 .spirv32,
6093 .dxil,
6094 .loongarch32,
6095 .xtensa,
6096 => 32,
6097
6098 .amdgcn,
6099 .bpfel,
6100 .bpfeb,
6101 .le64,
6102 .mips64,
6103 .mips64el,
6104 .nvptx64,
6105 .powerpc64,
6106 .powerpc64le,
6107 .riscv64,
6108 .sparc64,
6109 .s390x,
6110 .amdil64,
6111 .hsail64,
6112 .spir64,
6113 .wasm64,
6114 .renderscript64,
6115 .ve,
6116 .spirv64,
6117 .loongarch64,
6118 => 64,
6119
6120 .aarch64,
6121 .aarch64_be,
6122 .aarch64_32,
6123 => 128,
6124
6125 .x86_64 => if (std.Target.x86.featureSetHas(target.cpu.features, .cx16)) 128 else 64,
6126
6127 .spirv => @panic("TODO what should this value be?"),
6128 };
6129
6130 const int_ty = switch (ty.zigTypeTag(mod)) {
6131 .Int => ty,
6132 .Enum => ty.intTagType(mod),
6133 .Float => {
6134 const bit_count = ty.floatBits(target);
6135 if (bit_count > max_atomic_bits) {
6136 diags.* = .{
6137 .bits = bit_count,
6138 .max_bits = max_atomic_bits,
6139 };
6140 return error.FloatTooBig;
6141 }
6142 return .none;
6143 },
6144 .Bool => return .none,
6145 else => {
6146 if (ty.isPtrAtRuntime(mod)) return .none;
6147 return error.BadType;
6148 },
6149 };
6150
6151 const bit_count = int_ty.intInfo(mod).bits;
6152 if (bit_count > max_atomic_bits) {
6153 diags.* = .{
6154 .bits = bit_count,
6155 .max_bits = max_atomic_bits,
6156 };
6157 return error.IntTooBig;
6158 }
6159
6160 return .none;
6161}
6162
6163pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
6164 return mod.declPtr(decl_index).getFileScope(mod);
6165}
6166
6167/// Returns null in the following cases:
6168/// * `@TypeOf(.{})`
6169/// * A struct which has no fields (`struct {}`).
6170/// * Not a struct.
6171pub fn typeToStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
6172 if (ty.ip_index == .none) return null;
6173 const ip = &mod.intern_pool;
6174 return switch (ip.indexToKey(ty.ip_index)) {
6175 .struct_type => ip.loadStructType(ty.ip_index),
6176 else => null,
6177 };
6178}
6179
6180pub fn typeToPackedStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType {
6181 const s = mod.typeToStruct(ty) orelse return null;
6182 if (s.layout != .@"packed") return null;
6183 return s;
6184}
6185
6186pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.LoadedUnionType {
6187 if (ty.ip_index == .none) return null;
6188 const ip = &mod.intern_pool;
6189 return switch (ip.indexToKey(ty.ip_index)) {
6190 .union_type => ip.loadUnionType(ty.ip_index),
6191 else => null,
6192 };
6193}
6194
6195pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType {
6196 if (ty.ip_index == .none) return null;
6197 return mod.intern_pool.indexToFuncType(ty.toIntern());
6198}
6199
6200pub fn funcOwnerDeclPtr(mod: *Module, func_index: InternPool.Index) *Decl {
6201 return mod.declPtr(mod.funcOwnerDeclIndex(func_index));
6202}
6203
6204pub fn funcOwnerDeclIndex(mod: *Module, func_index: InternPool.Index) Decl.Index {
6205 return mod.funcInfo(func_index).owner_decl;
6206}
6207
6208pub fn iesFuncIndex(mod: *const Module, ies_index: InternPool.Index) InternPool.Index {
6209 return mod.intern_pool.iesFuncIndex(ies_index);
6210}
6211
6212pub fn funcInfo(mod: *Module, func_index: InternPool.Index) InternPool.Key.Func {
6213 return mod.intern_pool.indexToKey(func_index).func;
6214}
6215
6216pub fn toEnum(mod: *Module, comptime E: type, val: Value) E {
6217 return mod.intern_pool.toEnum(E, val.toIntern());
6218}
6219
6220pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool {
6221 const file = mod.declPtr(func.owner_decl).getFileScope(mod);
6222
6223 const tags = file.zir.instructions.items(.tag);
6224
6225 const param_body = file.zir.getParamBody(func.zir_body_inst);
6226 const param = param_body[index];
6227
6228 return switch (tags[param]) {
6229 .param, .param_comptime => false,
6230 .param_anytype, .param_anytype_comptime => true,
6231 else => unreachable,
6232 };
6233}
6234
6235pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]const u8 {
6236 const func = mod.funcInfo(func_index);
6237 const file = mod.declPtr(func.owner_decl).getFileScope(mod);
6238
6239 const tags = file.zir.instructions.items(.tag);
6240 const data = file.zir.instructions.items(.data);
6241
6242 const param_body = file.zir.getParamBody(func.zir_body_inst.resolve(&mod.intern_pool));
6243 const param = param_body[index];
6244
6245 return switch (tags[@intFromEnum(param)]) {
6246 .param, .param_comptime => blk: {
6247 const extra = file.zir.extraData(Zir.Inst.Param, data[@intFromEnum(param)].pl_tok.payload_index);
6248 break :blk file.zir.nullTerminatedString(extra.data.name);
6249 },
6250 .param_anytype, .param_anytype_comptime => blk: {
6251 const param_data = data[@intFromEnum(param)].str_tok;
6252 break :blk param_data.get(file.zir);
6253 },
6254 else => unreachable,
6255 };
6256}
6257
6258pub const UnionLayout = struct {
6259 abi_size: u64,
6260 abi_align: Alignment,
6261 most_aligned_field: u32,
6262 most_aligned_field_size: u64,
6263 biggest_field: u32,
6264 payload_size: u64,
6265 payload_align: Alignment,
6266 tag_align: Alignment,
6267 tag_size: u64,
6268 padding: u32,
6269};
6270
6271pub fn getUnionLayout(mod: *Module, loaded_union: InternPool.LoadedUnionType) UnionLayout {
6272 const ip = &mod.intern_pool;
6273 assert(loaded_union.haveLayout(ip));
6274 var most_aligned_field: u32 = undefined;
6275 var most_aligned_field_size: u64 = undefined;
6276 var biggest_field: u32 = undefined;
6277 var payload_size: u64 = 0;
6278 var payload_align: Alignment = .@"1";
6279 for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
6280 if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue;
6281
6282 const explicit_align = loaded_union.fieldAlign(ip, field_index);
6283 const field_align = if (explicit_align != .none)
6284 explicit_align
6285 else
6286 Type.fromInterned(field_ty).abiAlignment(mod);
6287 const field_size = Type.fromInterned(field_ty).abiSize(mod);
6288 if (field_size > payload_size) {
6289 payload_size = field_size;
6290 biggest_field = @intCast(field_index);
6291 }
6292 if (field_align.compare(.gte, payload_align)) {
6293 payload_align = field_align;
6294 most_aligned_field = @intCast(field_index);
6295 most_aligned_field_size = field_size;
6296 }
6297 }
6298 const have_tag = loaded_union.flagsPtr(ip).runtime_tag.hasTag();
6299 if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(mod)) {
6300 return .{
6301 .abi_size = payload_align.forward(payload_size),
6302 .abi_align = payload_align,
6303 .most_aligned_field = most_aligned_field,
6304 .most_aligned_field_size = most_aligned_field_size,
6305 .biggest_field = biggest_field,
6306 .payload_size = payload_size,
6307 .payload_align = payload_align,
6308 .tag_align = .none,
6309 .tag_size = 0,
6310 .padding = 0,
6311 };
6312 }
6313
6314 const tag_size = Type.fromInterned(loaded_union.enum_tag_ty).abiSize(mod);
6315 const tag_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(mod).max(.@"1");
6316 return .{
6317 .abi_size = loaded_union.size(ip).*,
6318 .abi_align = tag_align.max(payload_align),
6319 .most_aligned_field = most_aligned_field,
6320 .most_aligned_field_size = most_aligned_field_size,
6321 .biggest_field = biggest_field,
6322 .payload_size = payload_size,
6323 .payload_align = payload_align,
6324 .tag_align = tag_align,
6325 .tag_size = tag_size,
6326 .padding = loaded_union.padding(ip).*,
6327 };
6328}
6329
6330pub fn unionAbiSize(mod: *Module, loaded_union: InternPool.LoadedUnionType) u64 {
6331 return mod.getUnionLayout(loaded_union).abi_size;
6332}
6333
6334/// Returns 0 if the union is represented with 0 bits at runtime.
6335pub fn unionAbiAlignment(mod: *Module, loaded_union: InternPool.LoadedUnionType) Alignment {
6336 const ip = &mod.intern_pool;
6337 const have_tag = loaded_union.flagsPtr(ip).runtime_tag.hasTag();
6338 var max_align: Alignment = .none;
6339 if (have_tag) max_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(mod);
6340 for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
6341 if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue;
6342
6343 const field_align = mod.unionFieldNormalAlignment(loaded_union, @intCast(field_index));
6344 max_align = max_align.max(field_align);
6345 }
6346 return max_align;
6347}
6348
6349/// Returns the field alignment, assuming the union is not packed.
6350/// Keep implementation in sync with `Sema.unionFieldAlignment`.
6351/// Prefer to call that function instead of this one during Sema.
6352pub fn unionFieldNormalAlignment(mod: *Module, loaded_union: InternPool.LoadedUnionType, field_index: u32) Alignment {
6353 const ip = &mod.intern_pool;
6354 const field_align = loaded_union.fieldAlign(ip, field_index);
6355 if (field_align != .none) return field_align;
6356 const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]);
6357 return field_ty.abiAlignment(mod);
6358}
6359
6360/// Returns the index of the active field, given the current tag value
6361pub fn unionTagFieldIndex(mod: *Module, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
6362 const ip = &mod.intern_pool;
6363 if (enum_tag.toIntern() == .none) return null;
6364 assert(ip.typeOf(enum_tag.toIntern()) == loaded_union.enum_tag_ty);
6365 return loaded_union.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern());
6366}
6367
6368/// Returns the field alignment of a non-packed struct in byte units.
6369/// Keep implementation in sync with `Sema.structFieldAlignment`.
6370/// asserts the layout is not packed.
6371pub fn structFieldAlignment(
6372 mod: *Module,
6373 explicit_alignment: InternPool.Alignment,
6374 field_ty: Type,
6375 layout: std.builtin.Type.ContainerLayout,
6376) Alignment {
6377 assert(layout != .@"packed");
6378 if (explicit_alignment != .none) return explicit_alignment;
6379 switch (layout) {
6380 .@"packed" => unreachable,
6381 .auto => {
6382 if (mod.getTarget().ofmt == .c) {
6383 return structFieldAlignmentExtern(mod, field_ty);
6384 } else {
6385 return field_ty.abiAlignment(mod);
6386 }
6387 },
6388 .@"extern" => return structFieldAlignmentExtern(mod, field_ty),
6389 }
6390}
6391
6392/// Returns the field alignment of an extern struct in byte units.
6393/// This logic is duplicated in Type.abiAlignmentAdvanced.
6394pub fn structFieldAlignmentExtern(mod: *Module, field_ty: Type) Alignment {
6395 const ty_abi_align = field_ty.abiAlignment(mod);
6396
6397 if (field_ty.isAbiInt(mod) and field_ty.intInfo(mod).bits >= 128) {
6398 // The C ABI requires 128 bit integer fields of structs
6399 // to be 16-bytes aligned.
6400 return ty_abi_align.max(.@"16");
6401 }
6402
6403 return ty_abi_align;
6404}
6405
6406/// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets
6407/// into the packed struct InternPool data rather than computing this on the
6408/// fly, however it was found to perform worse when measured on real world
6409/// projects.
6410pub fn structPackedFieldBitOffset(
6411 mod: *Module,
6412 struct_type: InternPool.LoadedStructType,
6413 field_index: u32,
6414) u16 {
6415 const ip = &mod.intern_pool;
6416 assert(struct_type.layout == .@"packed");
6417 assert(struct_type.haveLayout(ip));
6418 var bit_sum: u64 = 0;
6419 for (0..struct_type.field_types.len) |i| {
6420 if (i == field_index) {
6421 return @intCast(bit_sum);
6422 }
6423 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
6424 bit_sum += field_ty.bitSize(mod);
6425 }
6426 unreachable; // index out of bounds
6427}
src/arch/aarch64/CodeGen.zig+3-1
......@@ -11,7 +11,9 @@ const Liveness = @import("../../Liveness.zig");
1111const Type = @import("../../type.zig").Type;
1212const Value = @import("../../Value.zig");
1313const link = @import("../../link.zig");
14const Module = @import("../../Module.zig");
14const Zcu = @import("../../Zcu.zig");
15/// Deprecated.
16const Module = Zcu;
1517const InternPool = @import("../../InternPool.zig");
1618const Compilation = @import("../../Compilation.zig");
1719const ErrorMsg = Module.ErrorMsg;
src/arch/aarch64/Emit.zig+3-1
......@@ -7,7 +7,9 @@ const math = std.math;
77const Mir = @import("Mir.zig");
88const bits = @import("bits.zig");
99const link = @import("../../link.zig");
10const Module = @import("../../Module.zig");
10const Zcu = @import("../../Zcu.zig");
11/// Deprecated.
12const Module = Zcu;
1113const ErrorMsg = Module.ErrorMsg;
1214const assert = std.debug.assert;
1315const Instruction = bits.Instruction;
src/arch/aarch64/abi.zig+3-1
......@@ -4,7 +4,9 @@ const bits = @import("bits.zig");
44const Register = bits.Register;
55const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
66const Type = @import("../../type.zig").Type;
7const Module = @import("../../Module.zig");
7const Zcu = @import("../../Zcu.zig");
8/// Deprecated.
9const Module = Zcu;
810
911pub const Class = union(enum) {
1012 memory,
src/arch/arm/CodeGen.zig+3-1
......@@ -11,7 +11,9 @@ const Liveness = @import("../../Liveness.zig");
1111const Type = @import("../../type.zig").Type;
1212const Value = @import("../../Value.zig");
1313const link = @import("../../link.zig");
14const Module = @import("../../Module.zig");
14const Zcu = @import("../../Zcu.zig");
15/// Deprecated.
16const Module = Zcu;
1517const InternPool = @import("../../InternPool.zig");
1618const Compilation = @import("../../Compilation.zig");
1719const ErrorMsg = Module.ErrorMsg;
src/arch/arm/Emit.zig+3-1
......@@ -8,7 +8,9 @@ const math = std.math;
88const Mir = @import("Mir.zig");
99const bits = @import("bits.zig");
1010const link = @import("../../link.zig");
11const Module = @import("../../Module.zig");
11const Zcu = @import("../../Zcu.zig");
12/// Deprecated.
13const Module = Zcu;
1214const Type = @import("../../type.zig").Type;
1315const ErrorMsg = Module.ErrorMsg;
1416const Target = std.Target;
src/arch/arm/abi.zig+3-1
......@@ -4,7 +4,9 @@ const bits = @import("bits.zig");
44const Register = bits.Register;
55const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
66const Type = @import("../../type.zig").Type;
7const Module = @import("../../Module.zig");
7const Zcu = @import("../../Zcu.zig");
8/// Deprecated.
9const Module = Zcu;
810
911pub const Class = union(enum) {
1012 memory,
src/arch/riscv64/CodeGen.zig+3-1
......@@ -10,7 +10,9 @@ const Liveness = @import("../../Liveness.zig");
1010const Type = @import("../../type.zig").Type;
1111const Value = @import("../../Value.zig");
1212const link = @import("../../link.zig");
13const Module = @import("../../Module.zig");
13const Zcu = @import("../../Zcu.zig");
14/// Deprecated.
15const Module = Zcu;
1416const Package = @import("../../Package.zig");
1517const InternPool = @import("../../InternPool.zig");
1618const Compilation = @import("../../Compilation.zig");
src/arch/riscv64/Lower.zig+3-1
......@@ -522,6 +522,8 @@ const Air = @import("../../Air.zig");
522522const Allocator = std.mem.Allocator;
523523const ErrorMsg = Module.ErrorMsg;
524524const Mir = @import("Mir.zig");
525const Module = @import("../../Module.zig");
525const Zcu = @import("../../Zcu.zig");
526/// Deprecated.
527const Module = Zcu;
526528const Instruction = encoder.Instruction;
527529const Immediate = bits.Immediate;
src/arch/riscv64/abi.zig+3-1
......@@ -4,7 +4,9 @@ const Register = bits.Register;
44const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
55const Type = @import("../../type.zig").Type;
66const InternPool = @import("../../InternPool.zig");
7const Module = @import("../../Module.zig");
7const Zcu = @import("../../Zcu.zig");
8/// Deprecated.
9const Module = Zcu;
810const assert = std.debug.assert;
911
1012pub const Class = enum { memory, byval, integer, double_integer, fields };
src/arch/riscv64/bits.zig+3-1
......@@ -4,7 +4,9 @@ const assert = std.debug.assert;
44const testing = std.testing;
55const Target = std.Target;
66
7const Module = @import("../../Module.zig");
7const Zcu = @import("../../Zcu.zig");
8/// Deprecated.
9const Module = Zcu;
810const Encoding = @import("Encoding.zig");
911const Mir = @import("Mir.zig");
1012const abi = @import("abi.zig");
src/arch/sparc64/CodeGen.zig+3-1
......@@ -10,7 +10,9 @@ const mem = std.mem;
1010const Allocator = mem.Allocator;
1111const builtin = @import("builtin");
1212const link = @import("../../link.zig");
13const Module = @import("../../Module.zig");
13const Zcu = @import("../../Zcu.zig");
14/// Deprecated.
15const Module = Zcu;
1416const InternPool = @import("../../InternPool.zig");
1517const Value = @import("../../Value.zig");
1618const ErrorMsg = Module.ErrorMsg;
src/arch/sparc64/Emit.zig+3-1
......@@ -5,7 +5,9 @@ const std = @import("std");
55const Endian = std.builtin.Endian;
66const assert = std.debug.assert;
77const link = @import("../../link.zig");
8const Module = @import("../../Module.zig");
8const Zcu = @import("../../Zcu.zig");
9/// Deprecated.
10const Module = Zcu;
911const ErrorMsg = Module.ErrorMsg;
1012const Liveness = @import("../../Liveness.zig");
1113const log = std.log.scoped(.sparcv9_emit);
src/arch/wasm/CodeGen.zig+3-1
......@@ -10,7 +10,9 @@ const wasm = std.wasm;
1010const log = std.log.scoped(.codegen);
1111
1212const codegen = @import("../../codegen.zig");
13const Module = @import("../../Module.zig");
13const Zcu = @import("../../Zcu.zig");
14/// Deprecated.
15const Module = Zcu;
1416const InternPool = @import("../../InternPool.zig");
1517const Decl = Module.Decl;
1618const Type = @import("../../type.zig").Type;
src/arch/wasm/Emit.zig+3-1
......@@ -5,7 +5,9 @@ const Emit = @This();
55const std = @import("std");
66const Mir = @import("Mir.zig");
77const link = @import("../../link.zig");
8const Module = @import("../../Module.zig");
8const Zcu = @import("../../Zcu.zig");
9/// Deprecated.
10const Module = Zcu;
911const InternPool = @import("../../InternPool.zig");
1012const codegen = @import("../../codegen.zig");
1113const leb128 = std.leb;
src/arch/wasm/abi.zig+3-1
......@@ -9,7 +9,9 @@ const Target = std.Target;
99const assert = std.debug.assert;
1010
1111const Type = @import("../../type.zig").Type;
12const Module = @import("../../Module.zig");
12const Zcu = @import("../../Zcu.zig");
13/// Deprecated.
14const Module = Zcu;
1315
1416/// Defines how to pass a type as part of a function signature,
1517/// both for parameters as well as return values.
src/arch/x86_64/CodeGen.zig+3-2
......@@ -26,8 +26,9 @@ const Liveness = @import("../../Liveness.zig");
2626const Lower = @import("Lower.zig");
2727const Mir = @import("Mir.zig");
2828const Package = @import("../../Package.zig");
29const Module = @import("../../Module.zig");
30const Zcu = Module;
29const Zcu = @import("../../Zcu.zig");
30/// Deprecated.
31const Module = Zcu;
3132const InternPool = @import("../../InternPool.zig");
3233const Alignment = InternPool.Alignment;
3334const Target = std.Target;
src/arch/x86_64/Lower.zig+3-1
......@@ -664,7 +664,9 @@ const Lower = @This();
664664const Memory = Instruction.Memory;
665665const Mir = @import("Mir.zig");
666666const Mnemonic = Instruction.Mnemonic;
667const Module = @import("../../Module.zig");
667const Zcu = @import("../../Zcu.zig");
668/// Deprecated.
669const Module = Zcu;
668670const Operand = Instruction.Operand;
669671const Prefix = Instruction.Prefix;
670672const Register = bits.Register;
src/arch/x86_64/abi.zig+1-1
......@@ -539,4 +539,4 @@ const Register = @import("bits.zig").Register;
539539const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
540540const Type = @import("../../type.zig").Type;
541541const Value = @import("../../Value.zig");
542const Zcu = @import("../../Module.zig");
542const Zcu = @import("../../Zcu.zig");
src/codegen.zig+2-1
......@@ -16,7 +16,8 @@ const Compilation = @import("Compilation.zig");
1616const ErrorMsg = Module.ErrorMsg;
1717const InternPool = @import("InternPool.zig");
1818const Liveness = @import("Liveness.zig");
19const Zcu = @import("Module.zig");
19const Zcu = @import("Zcu.zig");
20/// Deprecated.
2021const Module = Zcu;
2122const Target = std.Target;
2223const Type = @import("type.zig").Type;
src/codegen/c.zig+1-1
......@@ -5,7 +5,7 @@ const mem = std.mem;
55const log = std.log.scoped(.c);
66
77const link = @import("../link.zig");
8const Zcu = @import("../Module.zig");
8const Zcu = @import("../Zcu.zig");
99const Module = @import("../Package/Module.zig");
1010const Compilation = @import("../Compilation.zig");
1111const Value = @import("../Value.zig");
src/codegen/c/Type.zig+1-1
......@@ -2584,5 +2584,5 @@ const CType = @This();
25842584const Module = @import("../../Package/Module.zig");
25852585const std = @import("std");
25862586const Type = @import("../../type.zig").Type;
2587const Zcu = @import("../../Module.zig");
2587const Zcu = @import("../../Zcu.zig");
25882588const DeclIndex = @import("../../InternPool.zig").DeclIndex;
src/codegen/llvm.zig+3-2
......@@ -14,8 +14,9 @@ else
1414const link = @import("../link.zig");
1515const Compilation = @import("../Compilation.zig");
1616const build_options = @import("build_options");
17const Module = @import("../Module.zig");
18const Zcu = Module;
17const Zcu = @import("../Zcu.zig");
18/// Deprecated.
19const Module = Zcu;
1920const InternPool = @import("../InternPool.zig");
2021const Package = @import("../Package.zig");
2122const Air = @import("../Air.zig");
src/codegen/spirv.zig+3-1
......@@ -5,7 +5,9 @@ const log = std.log.scoped(.codegen);
55const assert = std.debug.assert;
66const Signedness = std.builtin.Signedness;
77
8const Module = @import("../Module.zig");
8const Zcu = @import("../Zcu.zig");
9/// Deprecated.
10const Module = Zcu;
911const Decl = Module.Decl;
1012const Type = @import("../type.zig").Type;
1113const Value = @import("../Value.zig");
src/crash_report.zig+5-5
......@@ -8,11 +8,11 @@ const windows = std.os.windows;
88const posix = std.posix;
99const native_os = builtin.os.tag;
1010
11const Module = @import("Module.zig");
11const Zcu = @import("Zcu.zig");
1212const Sema = @import("Sema.zig");
1313const InternPool = @import("InternPool.zig");
1414const Zir = std.zig.Zir;
15const Decl = Module.Decl;
15const Decl = Zcu.Decl;
1616
1717/// To use these crash report diagnostics, publish this panic in your main file
1818/// and add `pub const enable_segfault_handler = false;` to your `std_options`.
......@@ -78,7 +78,7 @@ fn dumpStatusReport() !void {
7878 const block: *Sema.Block = anal.block;
7979 const mod = anal.sema.mod;
8080
81 const file, const src_base_node = Module.LazySrcLoc.resolveBaseNode(block.src_base_inst, mod);
81 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, mod);
8282
8383 try stderr.writeAll("Analyzing ");
8484 try writeFilePath(file, stderr);
......@@ -104,7 +104,7 @@ fn dumpStatusReport() !void {
104104 while (parent) |curr| {
105105 fba.reset();
106106 try stderr.writeAll(" in ");
107 const cur_block_file, const cur_block_src_base_node = Module.LazySrcLoc.resolveBaseNode(curr.block.src_base_inst, mod);
107 const cur_block_file, const cur_block_src_base_node = Zcu.LazySrcLoc.resolveBaseNode(curr.block.src_base_inst, mod);
108108 try writeFilePath(cur_block_file, stderr);
109109 try stderr.writeAll("\n > ");
110110 print_zir.renderSingleInstruction(
......@@ -128,7 +128,7 @@ fn dumpStatusReport() !void {
128128
129129var crash_heap: [16 * 4096]u8 = undefined;
130130
131fn writeFilePath(file: *Module.File, writer: anytype) !void {
131fn writeFilePath(file: *Zcu.File, writer: anytype) !void {
132132 if (file.mod.root.root_dir.path) |path| {
133133 try writer.writeAll(path);
134134 try writer.writeAll(std.fs.path.sep_str);
src/link.zig+3-1
......@@ -14,7 +14,9 @@ const Cache = std.Build.Cache;
1414const Compilation = @import("Compilation.zig");
1515const LibCInstallation = std.zig.LibCInstallation;
1616const Liveness = @import("Liveness.zig");
17const Module = @import("Module.zig");
17const Zcu = @import("Zcu.zig");
18/// Deprecated.
19const Module = Zcu;
1820const InternPool = @import("InternPool.zig");
1921const Type = @import("type.zig").Type;
2022const Value = @import("Value.zig");
src/link/C.zig+1-1
......@@ -6,7 +6,7 @@ const fs = std.fs;
66
77const C = @This();
88const build_options = @import("build_options");
9const Zcu = @import("../Module.zig");
9const Zcu = @import("../Zcu.zig");
1010const Module = @import("../Package/Module.zig");
1111const InternPool = @import("../InternPool.zig");
1212const Alignment = InternPool.Alignment;
src/link/Coff.zig+3-1
......@@ -2740,7 +2740,9 @@ const Compilation = @import("../Compilation.zig");
27402740const ImportTable = @import("Coff/ImportTable.zig");
27412741const Liveness = @import("../Liveness.zig");
27422742const LlvmObject = @import("../codegen/llvm.zig").Object;
2743const Module = @import("../Module.zig");
2743const Zcu = @import("../Zcu.zig");
2744/// Deprecated.
2745const Module = Zcu;
27442746const InternPool = @import("../InternPool.zig");
27452747const Object = @import("Coff/Object.zig");
27462748const Relocation = @import("Coff/Relocation.zig");
src/link/Dwarf.zig+3-1
......@@ -2964,7 +2964,9 @@ const File = link.File;
29642964const LinkBlock = File.LinkBlock;
29652965const LinkFn = File.LinkFn;
29662966const LinkerLoad = @import("../codegen.zig").LinkerLoad;
2967const Module = @import("../Module.zig");
2967const Zcu = @import("../Zcu.zig");
2968/// Deprecated.
2969const Module = Zcu;
29682970const InternPool = @import("../InternPool.zig");
29692971const StringTable = @import("StringTable.zig");
29702972const Type = @import("../type.zig").Type;
src/link/Elf.zig+3-1
......@@ -6466,7 +6466,9 @@ const Liveness = @import("../Liveness.zig");
64666466const LlvmObject = @import("../codegen/llvm.zig").Object;
64676467const MergeSection = merge_section.MergeSection;
64686468const MergeSubsection = merge_section.MergeSubsection;
6469const Module = @import("../Module.zig");
6469const Zcu = @import("../Zcu.zig");
6470/// Deprecated.
6471const Module = Zcu;
64706472const Object = @import("Elf/Object.zig");
64716473const InternPool = @import("../InternPool.zig");
64726474const PltSection = synthetic_sections.PltSection;
src/link/Elf/ZigObject.zig+3-1
......@@ -1648,7 +1648,9 @@ const Elf = @import("../Elf.zig");
16481648const File = @import("file.zig").File;
16491649const InternPool = @import("../../InternPool.zig");
16501650const Liveness = @import("../../Liveness.zig");
1651const Module = @import("../../Module.zig");
1651const Zcu = @import("../../Zcu.zig");
1652/// Deprecated.
1653const Module = Zcu;
16521654const Object = @import("Object.zig");
16531655const Symbol = @import("Symbol.zig");
16541656const StringTable = @import("../StringTable.zig");
src/link/MachO.zig+3-1
......@@ -4861,7 +4861,9 @@ const LibStub = tapi.LibStub;
48614861const Liveness = @import("../Liveness.zig");
48624862const LlvmObject = @import("../codegen/llvm.zig").Object;
48634863const Md5 = std.crypto.hash.Md5;
4864const Module = @import("../Module.zig");
4864const Zcu = @import("../Zcu.zig");
4865/// Deprecated.
4866const Module = Zcu;
48654867const InternPool = @import("../InternPool.zig");
48664868const RebaseSection = synthetic.RebaseSection;
48674869pub const Relocation = @import("MachO/Relocation.zig");
src/link/MachO/ZigObject.zig+3-1
......@@ -1587,7 +1587,9 @@ const InternPool = @import("../../InternPool.zig");
15871587const Liveness = @import("../../Liveness.zig");
15881588const MachO = @import("../MachO.zig");
15891589const Nlist = Object.Nlist;
1590const Module = @import("../../Module.zig");
1590const Zcu = @import("../../Zcu.zig");
1591/// Deprecated.
1592const Module = Zcu;
15911593const Object = @import("Object.zig");
15921594const Relocation = @import("Relocation.zig");
15931595const Symbol = @import("Symbol.zig");
src/link/NvPtx.zig+3-1
......@@ -12,7 +12,9 @@ const Allocator = std.mem.Allocator;
1212const assert = std.debug.assert;
1313const log = std.log.scoped(.link);
1414
15const Module = @import("../Module.zig");
15const Zcu = @import("../Zcu.zig");
16/// Deprecated.
17const Module = Zcu;
1618const InternPool = @import("../InternPool.zig");
1719const Compilation = @import("../Compilation.zig");
1820const link = @import("../link.zig");
src/link/Plan9.zig+3-3
......@@ -3,7 +3,9 @@
33
44const Plan9 = @This();
55const link = @import("../link.zig");
6const Module = @import("../Module.zig");
6const Zcu = @import("../Zcu.zig");
7/// Deprecated.
8const Module = Zcu;
79const InternPool = @import("../InternPool.zig");
810const Compilation = @import("../Compilation.zig");
911const aout = @import("Plan9/aout.zig");
......@@ -1046,8 +1048,6 @@ pub fn freeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) void {
10461048 const gpa = self.base.comp.gpa;
10471049 // TODO audit the lifetimes of decls table entries. It's possible to get
10481050 // freeDecl without any updateDecl in between.
1049 // However that is planned to change, see the TODO comment in Module.zig
1050 // in the deleteUnusedDecl function.
10511051 const mod = self.base.comp.module.?;
10521052 const decl = mod.declPtr(decl_index);
10531053 const is_fn = decl.val.isFuncBody(mod);
src/link/SpirV.zig+3-1
......@@ -27,7 +27,9 @@ const Allocator = std.mem.Allocator;
2727const assert = std.debug.assert;
2828const log = std.log.scoped(.link);
2929
30const Module = @import("../Module.zig");
30const Zcu = @import("../Zcu.zig");
31/// Deprecated.
32const Module = Zcu;
3133const InternPool = @import("../InternPool.zig");
3234const Compilation = @import("../Compilation.zig");
3335const link = @import("../link.zig");
src/link/Wasm.zig+3-1
......@@ -28,7 +28,9 @@ const File = @import("Wasm/file.zig").File;
2828const InternPool = @import("../InternPool.zig");
2929const Liveness = @import("../Liveness.zig");
3030const LlvmObject = @import("../codegen/llvm.zig").Object;
31const Module = @import("../Module.zig");
31const Zcu = @import("../Zcu.zig");
32/// Deprecated.
33const Module = Zcu;
3234const Object = @import("Wasm/Object.zig");
3335const Symbol = @import("Wasm/Symbol.zig");
3436const Type = @import("../type.zig").Type;
src/link/Wasm/ZigObject.zig+3-1
......@@ -1242,7 +1242,9 @@ const Dwarf = @import("../Dwarf.zig");
12421242const File = @import("file.zig").File;
12431243const InternPool = @import("../../InternPool.zig");
12441244const Liveness = @import("../../Liveness.zig");
1245const Module = @import("../../Module.zig");
1245const Zcu = @import("../../Zcu.zig");
1246/// Deprecated.
1247const Module = Zcu;
12461248const StringTable = @import("../StringTable.zig");
12471249const Symbol = @import("Symbol.zig");
12481250const Type = @import("../../type.zig").Type;
src/main.zig+3-1
......@@ -26,7 +26,9 @@ const wasi_libc = @import("wasi_libc.zig");
2626const Cache = std.Build.Cache;
2727const target_util = @import("target.zig");
2828const crash_report = @import("crash_report.zig");
29const Module = @import("Module.zig");
29const Zcu = @import("Zcu.zig");
30/// Deprecated.
31const Module = Zcu;
3032const AstGen = std.zig.AstGen;
3133const mingw = @import("mingw.zig");
3234const Server = std.zig.Server;
src/mutable_value.zig+1-1
......@@ -1,7 +1,7 @@
11const std = @import("std");
22const assert = std.debug.assert;
33const Allocator = std.mem.Allocator;
4const Zcu = @import("Module.zig");
4const Zcu = @import("Zcu.zig");
55const InternPool = @import("InternPool.zig");
66const Type = @import("type.zig").Type;
77const Value = @import("Value.zig");
src/print_air.zig+6-6
......@@ -2,14 +2,14 @@ const std = @import("std");
22const Allocator = std.mem.Allocator;
33const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
44
5const Module = @import("Module.zig");
5const Zcu = @import("Zcu.zig");
66const Value = @import("Value.zig");
77const Type = @import("type.zig").Type;
88const Air = @import("Air.zig");
99const Liveness = @import("Liveness.zig");
1010const InternPool = @import("InternPool.zig");
1111
12pub fn write(stream: anytype, module: *Module, air: Air, liveness: ?Liveness) void {
12pub fn write(stream: anytype, module: *Zcu, air: Air, liveness: ?Liveness) void {
1313 const instruction_bytes = air.instructions.len *
1414 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
1515 // the debug safety tag but we want to measure release size.
......@@ -55,7 +55,7 @@ pub fn write(stream: anytype, module: *Module, air: Air, liveness: ?Liveness) vo
5555pub fn writeInst(
5656 stream: anytype,
5757 inst: Air.Inst.Index,
58 module: *Module,
58 module: *Zcu,
5959 air: Air,
6060 liveness: ?Liveness,
6161) void {
......@@ -70,16 +70,16 @@ pub fn writeInst(
7070 writer.writeInst(stream, inst) catch return;
7171}
7272
73pub fn dump(module: *Module, air: Air, liveness: ?Liveness) void {
73pub fn dump(module: *Zcu, air: Air, liveness: ?Liveness) void {
7474 write(std.io.getStdErr().writer(), module, air, liveness);
7575}
7676
77pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: ?Liveness) void {
77pub fn dumpInst(inst: Air.Inst.Index, module: *Zcu, air: Air, liveness: ?Liveness) void {
7878 writeInst(std.io.getStdErr().writer(), inst, module, air, liveness);
7979}
8080
8181const Writer = struct {
82 module: *Module,
82 module: *Zcu,
8383 gpa: Allocator,
8484 air: Air,
8585 liveness: ?Liveness,
src/print_value.zig+2-1
......@@ -4,7 +4,8 @@
44const std = @import("std");
55const Type = @import("type.zig").Type;
66const Value = @import("Value.zig");
7const Zcu = @import("Module.zig");
7const Zcu = @import("Zcu.zig");
8/// Deprecated.
89const Module = Zcu;
910const Sema = @import("Sema.zig");
1011const InternPool = @import("InternPool.zig");
src/print_zir.zig+1-1
......@@ -6,7 +6,7 @@ const Ast = std.zig.Ast;
66const InternPool = @import("InternPool.zig");
77
88const Zir = std.zig.Zir;
9const Zcu = @import("Module.zig");
9const Zcu = @import("Zcu.zig");
1010const Module = Zcu;
1111const LazySrcLoc = Zcu.LazySrcLoc;
1212
src/register_manager.zig+3-1
......@@ -6,7 +6,9 @@ const Allocator = std.mem.Allocator;
66const Air = @import("Air.zig");
77const StaticBitSet = std.bit_set.StaticBitSet;
88const Type = @import("type.zig").Type;
9const Module = @import("Module.zig");
9const Zcu = @import("Zcu.zig");
10/// Deprecated.
11const Module = Zcu;
1012const expect = std.testing.expect;
1113const expectEqual = std.testing.expectEqual;
1214const expectEqualSlices = std.testing.expectEqualSlices;
src/target.zig+1-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22const Type = @import("type.zig").Type;
33const AddressSpace = std.builtin.AddressSpace;
44const Alignment = @import("InternPool.zig").Alignment;
5const Feature = @import("Module.zig").Feature;
5const Feature = @import("Zcu.zig").Feature;
66
77pub const default_stack_protector_buffer_size = 4;
88
src/type.zig+3-2
......@@ -3,8 +3,9 @@ const builtin = @import("builtin");
33const Value = @import("Value.zig");
44const assert = std.debug.assert;
55const Target = std.Target;
6const Module = @import("Module.zig");
7const Zcu = Module;
6const Zcu = @import("Zcu.zig");
7/// Deprecated.
8const Module = Zcu;
89const log = std.log.scoped(.Type);
910const target_util = @import("target.zig");
1011const Sema = @import("Sema.zig");